snowflake-connector-python 3.16.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/connector/__init__.py +96 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +70 -0
- snowflake/connector/arrow_context.py +203 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +586 -0
- snowflake/connector/auth/_http_server.py +220 -0
- snowflake/connector/auth/_oauth_base.py +400 -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 +222 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +479 -0
- snowflake/connector/auth/oauth_credentials.py +66 -0
- snowflake/connector/auth/okta.py +335 -0
- snowflake/connector/auth/pat.py +39 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +502 -0
- snowflake/connector/auth/workload_identity.py +94 -0
- snowflake/connector/azure_storage_client.py +276 -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 +496 -0
- snowflake/connector/connection.py +2273 -0
- snowflake/connector/connection_diagnostic.py +776 -0
- snowflake/connector/constants.py +442 -0
- snowflake/connector/converter.py +785 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/cursor.py +1951 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +19 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +91 -0
- snowflake/connector/errors.py +615 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +27 -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 +1215 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +474 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +60 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp313-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 +557 -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 +1005 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -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 +71 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -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 +65 -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 +256 -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 +1297 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1883 -0
- snowflake/connector/options.py +131 -0
- snowflake/connector/pandas_tools.py +732 -0
- snowflake/connector/proxy.py +43 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +786 -0
- snowflake/connector/result_set.py +319 -0
- snowflake/connector/s3_storage_client.py +605 -0
- snowflake/connector/secret_detector.py +181 -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 +134 -0
- snowflake/connector/storage_client.py +478 -0
- snowflake/connector/telemetry.py +249 -0
- snowflake/connector/telemetry_oob.py +543 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +402 -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 +49 -0
- snowflake/connector/util_text.py +301 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +169 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +537 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +315 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +79 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +140 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1033 -0
- snowflake/connector/vendored/requests/sessions.py +833 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1093 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +85 -0
- snowflake/connector/vendored/urllib3/_collections.py +355 -0
- snowflake/connector/vendored/urllib3/_version.py +2 -0
- snowflake/connector/vendored/urllib3/connection.py +572 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
- snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
- snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
- snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
- snowflake/connector/vendored/urllib3/exceptions.py +323 -0
- snowflake/connector/vendored/urllib3/fields.py +274 -0
- snowflake/connector/vendored/urllib3/filepost.py +98 -0
- snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
- snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
- snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
- snowflake/connector/vendored/urllib3/request.py +191 -0
- snowflake/connector/vendored/urllib3/response.py +885 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
- snowflake/connector/vendored/urllib3/util/connection.py +156 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
- snowflake/connector/vendored/urllib3/util/queue.py +22 -0
- snowflake/connector/vendored/urllib3/util/request.py +146 -0
- snowflake/connector/vendored/urllib3/util/response.py +107 -0
- snowflake/connector/vendored/urllib3/util/retry.py +620 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
- snowflake/connector/vendored/urllib3/util/url.py +435 -0
- snowflake/connector/vendored/urllib3/util/wait.py +152 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +407 -0
- snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
- snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
- snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""The secret detector detects sensitive information.
|
|
3
|
+
|
|
4
|
+
It masks secrets that might be leaked from two potential avenues
|
|
5
|
+
1. Out of Band Telemetry
|
|
6
|
+
2. Logging
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
from typing import NamedTuple
|
|
14
|
+
|
|
15
|
+
MIN_TOKEN_LEN = os.getenv("MIN_TOKEN_LEN", 32)
|
|
16
|
+
MIN_PWD_LEN = os.getenv("MIN_PWD_LEN", 8)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MaskedMessageData(NamedTuple):
|
|
20
|
+
is_masked: bool = False
|
|
21
|
+
masked_text: str | None = None
|
|
22
|
+
error_str: str | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SecretDetector(logging.Formatter):
|
|
26
|
+
AWS_KEY_PATTERN = re.compile(
|
|
27
|
+
r"(aws_key_id|aws_secret_key|access_key_id|secret_access_key)\s*=\s*'([^']+)'",
|
|
28
|
+
flags=re.IGNORECASE,
|
|
29
|
+
)
|
|
30
|
+
AWS_TOKEN_PATTERN = re.compile(
|
|
31
|
+
r'(accessToken|tempToken|keySecret)"\s*:\s*"([a-z0-9/+]{32,}={0,2})"',
|
|
32
|
+
flags=re.IGNORECASE,
|
|
33
|
+
)
|
|
34
|
+
SAS_TOKEN_PATTERN = re.compile(
|
|
35
|
+
r"(sig|signature|AWSAccessKeyId|password|passcode)=(?P<secret>[a-z0-9%/+]{16,})",
|
|
36
|
+
flags=re.IGNORECASE,
|
|
37
|
+
)
|
|
38
|
+
PRIVATE_KEY_PATTERN = re.compile(
|
|
39
|
+
r"-{3,}BEGIN [A-Z ]*PRIVATE KEY-{3,}\n([\s\S]*?)\n-{3,}END [A-Z ]*PRIVATE KEY-{3,}",
|
|
40
|
+
flags=re.MULTILINE | re.IGNORECASE,
|
|
41
|
+
)
|
|
42
|
+
PRIVATE_KEY_DATA_PATTERN = re.compile(
|
|
43
|
+
r'"privateKeyData": "([a-z0-9/+=\\n]{10,})"', flags=re.MULTILINE | re.IGNORECASE
|
|
44
|
+
)
|
|
45
|
+
CONNECTION_TOKEN_PATTERN = re.compile(
|
|
46
|
+
r"(token|assertion content)" r"([\'\"\s:=]+)" r"([a-z0-9=/_\-\+\.]{8,})",
|
|
47
|
+
flags=re.IGNORECASE,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
PASSWORD_PATTERN = re.compile(
|
|
51
|
+
r"(password"
|
|
52
|
+
r"|pwd)"
|
|
53
|
+
r"([\'\"\s:=]+)"
|
|
54
|
+
r"([a-z0-9!\"#\$%&\\\'\(\)\*\+\,-\./:;<=>\?\@\[\]\^_`\{\|\}~]{1,})",
|
|
55
|
+
flags=re.IGNORECASE,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
SECRET_STARRED_MASK_STR = "****"
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def mask_connection_token(text: str) -> str:
|
|
62
|
+
return SecretDetector.CONNECTION_TOKEN_PATTERN.sub(
|
|
63
|
+
r"\1\2" + f"{SecretDetector.SECRET_STARRED_MASK_STR}", text
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def mask_password(text: str) -> str:
|
|
68
|
+
return SecretDetector.PASSWORD_PATTERN.sub(
|
|
69
|
+
r"\1\2" + f"{SecretDetector.SECRET_STARRED_MASK_STR}", text
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def mask_aws_keys(text: str) -> str:
|
|
74
|
+
return SecretDetector.AWS_KEY_PATTERN.sub(
|
|
75
|
+
r"\1=" + f"'{SecretDetector.SECRET_STARRED_MASK_STR}'", text
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def mask_sas_tokens(text: str) -> str:
|
|
80
|
+
return SecretDetector.SAS_TOKEN_PATTERN.sub(
|
|
81
|
+
r"\1=" + f"{SecretDetector.SECRET_STARRED_MASK_STR}", text
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def mask_aws_tokens(text: str) -> str:
|
|
86
|
+
return SecretDetector.AWS_TOKEN_PATTERN.sub(r'\1":"XXXX"', text)
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def mask_private_key(text: str) -> str:
|
|
90
|
+
return SecretDetector.PRIVATE_KEY_PATTERN.sub(
|
|
91
|
+
"-----BEGIN PRIVATE KEY-----\\\\nXXXX\\\\n-----END PRIVATE KEY-----", text
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def mask_private_key_data(text: str) -> str:
|
|
96
|
+
return SecretDetector.PRIVATE_KEY_DATA_PATTERN.sub(
|
|
97
|
+
'"privateKeyData": "XXXX"', text
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def mask_secrets(text: str) -> MaskedMessageData:
|
|
102
|
+
"""Masks any secrets. This is the method that should be used by outside classes.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
text: A string which may contain a secret.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The masked string data in MaskedMessageData.
|
|
109
|
+
"""
|
|
110
|
+
if text is None:
|
|
111
|
+
return MaskedMessageData()
|
|
112
|
+
|
|
113
|
+
masked = False
|
|
114
|
+
err_str = None
|
|
115
|
+
try:
|
|
116
|
+
masked_text = SecretDetector.mask_connection_token(
|
|
117
|
+
SecretDetector.mask_password(
|
|
118
|
+
SecretDetector.mask_private_key_data(
|
|
119
|
+
SecretDetector.mask_private_key(
|
|
120
|
+
SecretDetector.mask_aws_tokens(
|
|
121
|
+
SecretDetector.mask_sas_tokens(
|
|
122
|
+
SecretDetector.mask_aws_keys(text)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
if masked_text != text:
|
|
130
|
+
masked = True
|
|
131
|
+
except Exception as ex:
|
|
132
|
+
# We'll assume that the exception was raised during masking
|
|
133
|
+
# to be safe consider that the log has sensitive information
|
|
134
|
+
# and do not raise an exception.
|
|
135
|
+
masked = True
|
|
136
|
+
masked_text = str(ex)
|
|
137
|
+
err_str = str(ex)
|
|
138
|
+
|
|
139
|
+
return MaskedMessageData(masked, masked_text, err_str)
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def create_formatting_error_log(
|
|
143
|
+
original_record: logging.LogRecord, error_message: str
|
|
144
|
+
) -> str:
|
|
145
|
+
return "{} - {} {} - {} - {} - {}".format(
|
|
146
|
+
original_record.asctime,
|
|
147
|
+
original_record.threadName,
|
|
148
|
+
"secret_detector.py",
|
|
149
|
+
"sanitize_log_str",
|
|
150
|
+
original_record.levelname,
|
|
151
|
+
error_message,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
155
|
+
"""Wrapper around logging module's formatter.
|
|
156
|
+
|
|
157
|
+
This will ensure that the formatted message is free from sensitive credentials.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
record: The logging record.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Formatted desensitized log string.
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
unsanitized_log = super().format(record)
|
|
167
|
+
masked, optional_sanitized_log, err_str = SecretDetector.mask_secrets(
|
|
168
|
+
unsanitized_log
|
|
169
|
+
)
|
|
170
|
+
# Added to comply with type hints (Optional[str] is not accepted for str)
|
|
171
|
+
sanitized_log = optional_sanitized_log or ""
|
|
172
|
+
|
|
173
|
+
if masked and err_str is not None:
|
|
174
|
+
sanitized_log = self.create_formatting_error_log(record, err_str)
|
|
175
|
+
|
|
176
|
+
except Exception as ex:
|
|
177
|
+
sanitized_log = self.create_formatting_error_log(
|
|
178
|
+
record, "EXCEPTION - " + str(ex)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return sanitized_log
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
from typing import Protocol
|
|
7
|
+
|
|
8
|
+
from platformdirs import PlatformDirs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PlatformDirsProto(Protocol):
|
|
12
|
+
@property
|
|
13
|
+
def user_config_path(self) -> pathlib.Path: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _resolve_platform_dirs() -> PlatformDirsProto:
|
|
17
|
+
"""Decide on what PlatformDirs class to use.
|
|
18
|
+
|
|
19
|
+
In case a folder exists (which can be customized with the environmental
|
|
20
|
+
variable `SNOWFLAKE_HOME`) we use that directory as all platform
|
|
21
|
+
directories. If this folder does not exist we'll fall back to platformdirs
|
|
22
|
+
defaults.
|
|
23
|
+
|
|
24
|
+
This helper function was introduced to make this code testable.
|
|
25
|
+
"""
|
|
26
|
+
platformdir_kwargs = {
|
|
27
|
+
"appname": "snowflake",
|
|
28
|
+
"appauthor": False,
|
|
29
|
+
}
|
|
30
|
+
snowflake_home = pathlib.Path(
|
|
31
|
+
os.environ.get("SNOWFLAKE_HOME", "~/.snowflake/"),
|
|
32
|
+
).expanduser()
|
|
33
|
+
if snowflake_home.exists():
|
|
34
|
+
return SFPlatformDirs(
|
|
35
|
+
str(snowflake_home),
|
|
36
|
+
**platformdir_kwargs,
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
# In case SNOWFLAKE_HOME does not exist we fall back to using
|
|
40
|
+
# platformdirs to determine where system files should be placed. Please
|
|
41
|
+
# see docs for all the directories defined in the module at
|
|
42
|
+
# https://platformdirs.readthedocs.io/
|
|
43
|
+
return PlatformDirs(**platformdir_kwargs)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SFPlatformDirs:
|
|
47
|
+
"""Single folder platformdirs.
|
|
48
|
+
|
|
49
|
+
This class introduces a PlatformDir class where everything is placed into a
|
|
50
|
+
single folder. This is intended for users who prefer portability over all
|
|
51
|
+
else.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
single_dir: str,
|
|
57
|
+
**kwargs,
|
|
58
|
+
) -> None:
|
|
59
|
+
self.single_dir = pathlib.Path(single_dir)
|
|
60
|
+
|
|
61
|
+
@cached_property
|
|
62
|
+
def user_config_path(self) -> str:
|
|
63
|
+
"""data directory tied to to the user"""
|
|
64
|
+
return self.single_dir
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from base64 import b16decode, b16encode, standard_b64encode
|
|
5
|
+
|
|
6
|
+
from .errors import InternalError
|
|
7
|
+
|
|
8
|
+
# Converts a Snowflake binary value into a "bytes" object.
|
|
9
|
+
binary_to_python = b16decode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def binary_to_snowflake(binary_value) -> bytes | bytearray:
|
|
13
|
+
"""Encodes a "bytes" object for passing to Snowflake."""
|
|
14
|
+
result = b16encode(binary_value)
|
|
15
|
+
|
|
16
|
+
if isinstance(binary_value, bytearray):
|
|
17
|
+
return bytearray(result)
|
|
18
|
+
return result
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SnowflakeBinaryFormat:
|
|
22
|
+
"""Formats binary values ("bytes" objects) in hex or base64."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, name) -> None:
|
|
25
|
+
name = name.upper()
|
|
26
|
+
if name == "HEX":
|
|
27
|
+
self._encode = b16encode
|
|
28
|
+
elif name == "BASE64":
|
|
29
|
+
self._encode = standard_b64encode
|
|
30
|
+
else:
|
|
31
|
+
raise InternalError(f"Unrecognized binary format {name}")
|
|
32
|
+
|
|
33
|
+
def format(self, binary_value):
|
|
34
|
+
"""Formats a "bytes" object, returning a string."""
|
|
35
|
+
return self._encode(binary_value).decode("ascii")
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import time
|
|
5
|
+
from collections import namedtuple
|
|
6
|
+
from datetime import date, datetime, timedelta
|
|
7
|
+
from time import struct_time
|
|
8
|
+
|
|
9
|
+
ZERO_TIMEDELTA = timedelta(0)
|
|
10
|
+
|
|
11
|
+
ElementType = {
|
|
12
|
+
"Year2digit_ElementType": ["YY", "%y"],
|
|
13
|
+
"Year_ElementType": ["YYYY", "%Y"],
|
|
14
|
+
"Month_ElementType": ["MM", "%m"],
|
|
15
|
+
"MonthAbbrev_ElementType": ["MON", "%b"],
|
|
16
|
+
"DayOfMonth_ElementType": ["DD", "%d"],
|
|
17
|
+
"DayOfWeekAbbrev_ElementType": ["DY", "%a"],
|
|
18
|
+
"Hour24_ElementType": ["HH24", "%H"],
|
|
19
|
+
"Hour12_ElementType": ["HH12", "%I"],
|
|
20
|
+
"Hour_ElementType": ["HH", "%H"],
|
|
21
|
+
"Ante_Meridiem_ElementType": ["AM", "%p"],
|
|
22
|
+
"Post_Meridiem_ElementType": ["PM", "%p"],
|
|
23
|
+
"Minute_ElementType": ["MI", "%M"],
|
|
24
|
+
"Second_ElementType": ["SS", "%S"],
|
|
25
|
+
"MilliSecond_ElementType": ["FF", ""],
|
|
26
|
+
# special code for parsing fractions
|
|
27
|
+
"TZOffsetHourColonMin_ElementType": ["TZH:TZM", "%z"],
|
|
28
|
+
"TZOffsetHourMin_ElementType": ["TZHTZM", "%z"],
|
|
29
|
+
"TZOffsetHourOnly_ElementType": ["TZH", "%z"],
|
|
30
|
+
"TZAbbr_ElementType": ["TZD", "%Z"],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def sfdatetime_total_seconds_from_timedelta(td: timedelta) -> int:
|
|
35
|
+
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) // 10**6
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
SnowflakeDateTime = namedtuple("SnowflakeDateTime", "datetime nanosecond scale")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _support_negative_year(value: SnowflakeDateTime, year_len: int) -> str:
|
|
42
|
+
# if YYYY/YY is included
|
|
43
|
+
return _build_year_format(value.datetime, year_len)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _support_negative_year_datetime(value, year_len):
|
|
47
|
+
# if YYYY/YY is included
|
|
48
|
+
return _build_year_format(value, year_len)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _build_year_format(dt: datetime | struct_time, year_len: int) -> str:
|
|
52
|
+
if hasattr(dt, "year"):
|
|
53
|
+
# datetime
|
|
54
|
+
year_raw_value = dt.year
|
|
55
|
+
else:
|
|
56
|
+
# struct_time
|
|
57
|
+
year_raw_value = dt.tm_year
|
|
58
|
+
return _build_raw_year_format(year_raw_value, year_len)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _support_negative_year_struct_time(dt: struct_time, year_len: int) -> str:
|
|
62
|
+
# struct_time
|
|
63
|
+
return _build_raw_year_format(dt.tm_year, year_len)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _build_raw_year_format(year_raw_value: int, year_len: int) -> str:
|
|
67
|
+
sign_char = ""
|
|
68
|
+
if year_raw_value < 0:
|
|
69
|
+
sign_char = "-"
|
|
70
|
+
year_raw_value *= -1
|
|
71
|
+
if year_len == 2:
|
|
72
|
+
year_raw_value %= 100
|
|
73
|
+
fmt = sign_char + "{:0" + str(year_len) + "d}"
|
|
74
|
+
return fmt.format(year_raw_value)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _support_negative_year_date(value, year_len):
|
|
78
|
+
# if YYYY/YY is included
|
|
79
|
+
return _build_year_format(value, year_len)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _inject_fraction(value: SnowflakeDateTime | datetime, fraction_len: int) -> str:
|
|
83
|
+
# if FF is included
|
|
84
|
+
nano_str = "{:09d}"
|
|
85
|
+
|
|
86
|
+
if hasattr(value, "microsecond"):
|
|
87
|
+
nano_str = "{:06d}"
|
|
88
|
+
fraction = value.microsecond
|
|
89
|
+
elif hasattr(value, "nanosecond"):
|
|
90
|
+
fraction = value.nanosecond
|
|
91
|
+
else:
|
|
92
|
+
nano_str = "{:01d}"
|
|
93
|
+
fraction = 0 # struct_time. no fraction of second
|
|
94
|
+
|
|
95
|
+
if fraction_len > 0:
|
|
96
|
+
# truncate up to the specified length of FF
|
|
97
|
+
nano_value = nano_str.format(fraction)[:fraction_len]
|
|
98
|
+
else:
|
|
99
|
+
# no length of FF is specified
|
|
100
|
+
nano_value = nano_str.format(fraction)
|
|
101
|
+
if hasattr(value, "scale"):
|
|
102
|
+
# but scale is specified
|
|
103
|
+
nano_value = nano_value[: value.scale]
|
|
104
|
+
return nano_value
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _inject_others(_: SnowflakeDateTime | struct_time, value0: str) -> str:
|
|
108
|
+
return value0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
NOT_OTHER_FORMAT = {
|
|
112
|
+
_support_negative_year,
|
|
113
|
+
_support_negative_year_datetime,
|
|
114
|
+
_support_negative_year_struct_time,
|
|
115
|
+
_support_negative_year_date,
|
|
116
|
+
_inject_fraction,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class SnowflakeDateTimeFormat:
|
|
121
|
+
"""Snowflake DateTime Formatter."""
|
|
122
|
+
|
|
123
|
+
def __init__(
|
|
124
|
+
self,
|
|
125
|
+
sql_format,
|
|
126
|
+
data_type: str = "TIMESTAMP_NTZ",
|
|
127
|
+
datetime_class=datetime,
|
|
128
|
+
support_negative_year: bool = True,
|
|
129
|
+
inject_fraction: bool = True,
|
|
130
|
+
) -> None:
|
|
131
|
+
self._sql_format = sql_format
|
|
132
|
+
self._ignore_tz = data_type in ("TIMESTAMP_NTZ", "DATE")
|
|
133
|
+
if datetime_class == datetime:
|
|
134
|
+
self._support_negative_year_method = _support_negative_year_datetime
|
|
135
|
+
elif datetime_class == time.struct_time:
|
|
136
|
+
self._support_negative_year_method = _support_negative_year_struct_time
|
|
137
|
+
elif datetime_class == date:
|
|
138
|
+
self._support_negative_year_method = _support_negative_year_date
|
|
139
|
+
else:
|
|
140
|
+
self._support_negative_year_method = _support_negative_year
|
|
141
|
+
|
|
142
|
+
# format method
|
|
143
|
+
self.format = getattr(self, f"_format_{datetime_class.__name__}")
|
|
144
|
+
self._compile(
|
|
145
|
+
support_negative_year=support_negative_year, inject_fraction=inject_fraction
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def _pre_format(self, value: SnowflakeDateTime | struct_time) -> str:
|
|
149
|
+
fmt = []
|
|
150
|
+
for e in self._elements:
|
|
151
|
+
f = e[0]
|
|
152
|
+
fmt.append(f(value, e[1]))
|
|
153
|
+
return "".join(fmt)
|
|
154
|
+
|
|
155
|
+
def _format_SnowflakeDateTime(self, value: SnowflakeDateTime) -> str:
|
|
156
|
+
"""Formats SnowflakeDateTime object."""
|
|
157
|
+
fmt = self._pre_format(value)
|
|
158
|
+
dt = value.datetime
|
|
159
|
+
if isinstance(dt, time.struct_time):
|
|
160
|
+
return str(time.strftime(fmt, dt))
|
|
161
|
+
if dt.year < 1000:
|
|
162
|
+
# NOTE: still not supported
|
|
163
|
+
return dt.isoformat()
|
|
164
|
+
return dt.strftime(fmt)
|
|
165
|
+
|
|
166
|
+
def _format_datetime(self, value):
|
|
167
|
+
"""Formats datetime object."""
|
|
168
|
+
fmt = self._pre_format(value)
|
|
169
|
+
if isinstance(value, time.struct_time):
|
|
170
|
+
return str(time.strftime(fmt, value))
|
|
171
|
+
if value.year < 1000:
|
|
172
|
+
# NOTE: still not supported.
|
|
173
|
+
return value.isoformat()
|
|
174
|
+
return value.strftime(fmt)
|
|
175
|
+
|
|
176
|
+
def _match_token(self, sql_fmt, candidates, ignore: bool = False):
|
|
177
|
+
for c in candidates:
|
|
178
|
+
if sql_fmt.startswith(c[0]):
|
|
179
|
+
if not ignore:
|
|
180
|
+
self._elements.append((_inject_others, c[1]))
|
|
181
|
+
return len(c[0])
|
|
182
|
+
self._add_raw_char(sql_fmt[0])
|
|
183
|
+
return 1
|
|
184
|
+
|
|
185
|
+
def _add_raw_char(self, ch) -> None:
|
|
186
|
+
self._elements.append((_inject_others, "%%" if ch == "%" else ch))
|
|
187
|
+
|
|
188
|
+
def _compile(
|
|
189
|
+
self, support_negative_year: bool = True, inject_fraction: bool = True
|
|
190
|
+
) -> None:
|
|
191
|
+
self._elements = []
|
|
192
|
+
idx = 0
|
|
193
|
+
u_sql_format = self._sql_format.upper()
|
|
194
|
+
|
|
195
|
+
while idx < len(u_sql_format):
|
|
196
|
+
ch = u_sql_format[idx]
|
|
197
|
+
if ch == "A":
|
|
198
|
+
idx += self._match_token(
|
|
199
|
+
u_sql_format[idx:],
|
|
200
|
+
[
|
|
201
|
+
ElementType["Ante_Meridiem_ElementType"],
|
|
202
|
+
],
|
|
203
|
+
)
|
|
204
|
+
elif ch == "D":
|
|
205
|
+
idx += self._match_token(
|
|
206
|
+
u_sql_format[idx:],
|
|
207
|
+
[
|
|
208
|
+
ElementType["DayOfMonth_ElementType"],
|
|
209
|
+
ElementType["DayOfWeekAbbrev_ElementType"],
|
|
210
|
+
],
|
|
211
|
+
)
|
|
212
|
+
elif ch == "H":
|
|
213
|
+
idx += self._match_token(
|
|
214
|
+
u_sql_format[idx:],
|
|
215
|
+
[
|
|
216
|
+
ElementType["Hour24_ElementType"],
|
|
217
|
+
ElementType["Hour12_ElementType"],
|
|
218
|
+
ElementType["Hour_ElementType"],
|
|
219
|
+
],
|
|
220
|
+
)
|
|
221
|
+
elif ch == "M":
|
|
222
|
+
idx += self._match_token(
|
|
223
|
+
u_sql_format[idx:],
|
|
224
|
+
[
|
|
225
|
+
ElementType["MonthAbbrev_ElementType"],
|
|
226
|
+
ElementType["Month_ElementType"],
|
|
227
|
+
ElementType["Minute_ElementType"],
|
|
228
|
+
],
|
|
229
|
+
)
|
|
230
|
+
elif ch == "P":
|
|
231
|
+
idx += self._match_token(
|
|
232
|
+
u_sql_format[idx:],
|
|
233
|
+
[
|
|
234
|
+
ElementType["Post_Meridiem_ElementType"],
|
|
235
|
+
],
|
|
236
|
+
)
|
|
237
|
+
elif ch == "S":
|
|
238
|
+
idx += self._match_token(
|
|
239
|
+
u_sql_format[idx:],
|
|
240
|
+
[
|
|
241
|
+
ElementType["Second_ElementType"],
|
|
242
|
+
],
|
|
243
|
+
)
|
|
244
|
+
elif ch == "T":
|
|
245
|
+
# ignore TZ format if data type doesn't have TZ.
|
|
246
|
+
idx += self._match_token(
|
|
247
|
+
u_sql_format[idx:],
|
|
248
|
+
[
|
|
249
|
+
ElementType["TZOffsetHourColonMin_ElementType"],
|
|
250
|
+
ElementType["TZOffsetHourMin_ElementType"],
|
|
251
|
+
ElementType["TZOffsetHourOnly_ElementType"],
|
|
252
|
+
ElementType["TZAbbr_ElementType"],
|
|
253
|
+
],
|
|
254
|
+
ignore=self._ignore_tz,
|
|
255
|
+
)
|
|
256
|
+
elif ch == "Y":
|
|
257
|
+
idx += self._match_token(
|
|
258
|
+
u_sql_format[idx:],
|
|
259
|
+
[
|
|
260
|
+
ElementType["Year_ElementType"],
|
|
261
|
+
ElementType["Year2digit_ElementType"],
|
|
262
|
+
],
|
|
263
|
+
)
|
|
264
|
+
if support_negative_year:
|
|
265
|
+
# Add a special directive to handle YYYY/YY
|
|
266
|
+
last_element = self._elements[-1]
|
|
267
|
+
if last_element[1] == "%Y":
|
|
268
|
+
del self._elements[-1]
|
|
269
|
+
self._elements.append((self._support_negative_year_method, 4))
|
|
270
|
+
elif last_element[1] == "%y":
|
|
271
|
+
del self._elements[-1]
|
|
272
|
+
self._elements.append((self._support_negative_year_method, 2))
|
|
273
|
+
|
|
274
|
+
elif ch == ".":
|
|
275
|
+
if idx + 1 < len(u_sql_format) and u_sql_format[idx + 1 :].startswith(
|
|
276
|
+
ElementType["MilliSecond_ElementType"][0]
|
|
277
|
+
):
|
|
278
|
+
# Will be FF, just mark that there's a dot before FF
|
|
279
|
+
self._elements.append((_inject_others, "."))
|
|
280
|
+
self._fractions_with_dot = True
|
|
281
|
+
else:
|
|
282
|
+
self._add_raw_char(ch)
|
|
283
|
+
idx += 1
|
|
284
|
+
elif ch == "F":
|
|
285
|
+
if u_sql_format[idx:].startswith(
|
|
286
|
+
ElementType["MilliSecond_ElementType"][0]
|
|
287
|
+
):
|
|
288
|
+
idx += len(ElementType["MilliSecond_ElementType"][0])
|
|
289
|
+
if inject_fraction:
|
|
290
|
+
# Construct formatter to find fractions position.
|
|
291
|
+
fractions_len = -1
|
|
292
|
+
if idx < len(u_sql_format) and u_sql_format[idx].isdigit():
|
|
293
|
+
# followed by a single digit?
|
|
294
|
+
fractions_len = int(u_sql_format[idx])
|
|
295
|
+
idx += 1
|
|
296
|
+
self._elements.append((_inject_fraction, fractions_len))
|
|
297
|
+
else:
|
|
298
|
+
self._elements.append((_inject_others, "0"))
|
|
299
|
+
else:
|
|
300
|
+
self._add_raw_char(ch)
|
|
301
|
+
idx += 1
|
|
302
|
+
elif ch == '"':
|
|
303
|
+
# copy a double quoted string to the python format
|
|
304
|
+
idx += 1
|
|
305
|
+
start_idx = idx
|
|
306
|
+
while idx < len(self._sql_format) and self._sql_format[idx] != '"':
|
|
307
|
+
idx += 1
|
|
308
|
+
|
|
309
|
+
self._elements.append((_inject_others, self._sql_format[start_idx:idx]))
|
|
310
|
+
if idx < len(self._sql_format):
|
|
311
|
+
idx += 1
|
|
312
|
+
else:
|
|
313
|
+
self._add_raw_char(ch)
|
|
314
|
+
idx += 1
|
|
315
|
+
self._optimize_elements()
|
|
316
|
+
|
|
317
|
+
def _optimize_elements(self) -> None:
|
|
318
|
+
if len(self._elements) < 2:
|
|
319
|
+
return
|
|
320
|
+
last_element = self._elements[-1]
|
|
321
|
+
if last_element[0] in NOT_OTHER_FORMAT:
|
|
322
|
+
return
|
|
323
|
+
second_last_element = self._elements[-2]
|
|
324
|
+
if second_last_element[0] in NOT_OTHER_FORMAT:
|
|
325
|
+
return
|
|
326
|
+
del self._elements[-1]
|
|
327
|
+
del self._elements[-1]
|
|
328
|
+
self._elements.append(
|
|
329
|
+
(_inject_others, second_last_element[1] + last_element[1])
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class SnowflakeDateFormat(SnowflakeDateTimeFormat):
|
|
334
|
+
def __init__(self, sql_format, **kwargs) -> None:
|
|
335
|
+
kwargs["inject_fraction"] = False # no fraction
|
|
336
|
+
super().__init__(sql_format, **kwargs)
|
|
337
|
+
|
|
338
|
+
def _format_struct_time(self, value: struct_time) -> str:
|
|
339
|
+
"""Formats struct_time."""
|
|
340
|
+
fmt = self._pre_format(value)
|
|
341
|
+
return str(time.strftime(fmt, value))
|
|
342
|
+
|
|
343
|
+
def _format_date(self, value):
|
|
344
|
+
fmt = self._pre_format(value)
|
|
345
|
+
return value.strftime(fmt)
|