snowflake-connector-python 4.1.0__tar.gz → 4.1.1__tar.gz
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_python-4.1.0 → snowflake_connector_python-4.1.1}/DESCRIPTION.md +5 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/PKG-INFO +9 -2
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/setup.cfg +3 -1
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/connection.py +18 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/crl.py +131 -6
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/crl_cache.py +113 -22
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/network.py +3 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/result_batch.py +1 -1
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/session_manager.py +14 -8
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/version.py +1 -1
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/PKG-INFO +9 -2
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/requires.txt +9 -1
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/CONTRIBUTING.md +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/LICENSE.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/MANIFEST.in +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/NOTICE +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/README.md +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/SECURITY.md +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/pyproject.toml +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/setup.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/_query_context_cache.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/_sql_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/_utils.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/arrow_context.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/_auth.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/_http_server.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/_oauth_base.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/by_plugin.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/default.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/idtoken.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/keypair.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/no_auth.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/oauth.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/oauth_code.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/oauth_credentials.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/okta.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/pat.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/usrpwdmfa.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/webbrowser.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/auth/workload_identity.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/azure_storage_client.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/backoff_policies.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/bind_upload_agent.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/cache.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/compat.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/config_manager.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/connection_diagnostic.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/constants.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/converter.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/converter_issue23517.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/converter_null.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/converter_snowsql.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/cursor.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/dbapi.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/description.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/direct_file_operation_utils.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/encryption_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/errorcode.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/errors.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/externals_utils/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/externals_utils/externals_setup.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/feature.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/file_compression_type.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/file_lock.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/file_transfer_agent.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/file_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/gcs_storage_client.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/gzip_decoder.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/interval_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/local_storage_client.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/log_configuration.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/logging_utils/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/logging_utils/filters.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/ocsp_asn1crypto.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/ocsp_snowflake.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/options.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/pandas_tools.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/platform_detection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/proxy.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/py.typed +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/result_set.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/s3_storage_client.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/secret_detector.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/sf_dirs.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/sfbinaryformat.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/sfdatetime.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/snow_logging.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/sqlstate.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/ssd_internal_keys.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/ssl_wrap_socket.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/storage_client.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/telemetry.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/telemetry_oob.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/test_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/time_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/token_cache.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/tool/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/tool/dump_certs.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/tool/dump_ocsp_response.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/tool/dump_ocsp_response_cache.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/tool/probe_connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/url_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/util_text.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/LICENSE +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/__version__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/_internal_utils.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/adapters.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/api.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/auth.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/certs.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/compat.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/cookies.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/exceptions.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/help.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/hooks.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/models.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/packages.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/sessions.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/status_codes.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/structures.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/requests/utils.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/LICENSE.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/_base_connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/_collections.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/_request_methods.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/_version.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/connectionpool.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/emscripten_fetch_worker.js +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/fetch.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/request.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/emscripten/response.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/contrib/socks.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/exceptions.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/fields.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/filepost.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/http2/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/http2/connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/http2/probe.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/poolmanager.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/py.typed +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/response.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/__init__.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/connection.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/proxy.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/request.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/response.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/retry.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/ssl_.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/ssltransport.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/timeout.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/url.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/vendored/urllib3/util/wait.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/wif_util.py +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/SOURCES.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/dependency_links.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/entry_points.txt +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/not-zip-safe +0 -0
- {snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake_connector_python.egg-info/top_level.txt +0 -0
|
@@ -7,6 +7,11 @@ https://docs.snowflake.com/
|
|
|
7
7
|
Source code is also available at: https://github.com/snowflakedb/snowflake-connector-python
|
|
8
8
|
|
|
9
9
|
# Release Notes
|
|
10
|
+
- v4.1.1(TBD)
|
|
11
|
+
- Relaxed pandas dependency requirements for Python below 3.12.
|
|
12
|
+
- Changed CRL cache cleanup background task to daemon to avoid blocking main thread.
|
|
13
|
+
- Fixed NO_PROXY issues with PUT operations
|
|
14
|
+
|
|
10
15
|
- v4.1.0(November 18,2025)
|
|
11
16
|
- Added the `SNOWFLAKE_AUTH_FORCE_SERVER` environment variable to force the use of the local-listening server when using the `externalbrowser` auth method.
|
|
12
17
|
- This allows headless environments (like Docker or Airflow) running locally to auth via a browser URL.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snowflake-connector-python
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Snowflake Connector for Python
|
|
5
5
|
Home-page: https://www.snowflake.com/
|
|
6
6
|
Author: Snowflake, Inc
|
|
@@ -62,6 +62,7 @@ Requires-Dist: botocore>=1.24; extra == "boto"
|
|
|
62
62
|
Provides-Extra: development
|
|
63
63
|
Requires-Dist: Cython; extra == "development"
|
|
64
64
|
Requires-Dist: coverage; extra == "development"
|
|
65
|
+
Requires-Dist: mitmproxy>=6.0.0; python_version >= "3.10" and extra == "development"
|
|
65
66
|
Requires-Dist: more-itertools; extra == "development"
|
|
66
67
|
Requires-Dist: numpy<=2.2.4; extra == "development"
|
|
67
68
|
Requires-Dist: pendulum!=2.1.1; extra == "development"
|
|
@@ -74,7 +75,8 @@ Requires-Dist: pytest-xdist; extra == "development"
|
|
|
74
75
|
Requires-Dist: pytzdata; extra == "development"
|
|
75
76
|
Requires-Dist: responses; extra == "development"
|
|
76
77
|
Provides-Extra: pandas
|
|
77
|
-
Requires-Dist: pandas<3.0.0,>=
|
|
78
|
+
Requires-Dist: pandas<3.0.0,>=1.0.0; python_version < "3.13" and extra == "pandas"
|
|
79
|
+
Requires-Dist: pandas<3.0.0,>=2.1.2; python_version >= "3.13" and extra == "pandas"
|
|
78
80
|
Requires-Dist: pyarrow>=14.0.1; extra == "pandas"
|
|
79
81
|
Provides-Extra: secure-local-storage
|
|
80
82
|
Requires-Dist: keyring<26.0.0,>=23.1.0; extra == "secure-local-storage"
|
|
@@ -90,6 +92,11 @@ https://docs.snowflake.com/
|
|
|
90
92
|
Source code is also available at: https://github.com/snowflakedb/snowflake-connector-python
|
|
91
93
|
|
|
92
94
|
# Release Notes
|
|
95
|
+
- v4.1.1(TBD)
|
|
96
|
+
- Relaxed pandas dependency requirements for Python below 3.12.
|
|
97
|
+
- Changed CRL cache cleanup background task to daemon to avoid blocking main thread.
|
|
98
|
+
- Fixed NO_PROXY issues with PUT operations
|
|
99
|
+
|
|
93
100
|
- v4.1.0(November 18,2025)
|
|
94
101
|
- Added the `SNOWFLAKE_AUTH_FORCE_SERVER` environment variable to force the use of the local-listening server when using the `externalbrowser` auth method.
|
|
95
102
|
- This allows headless environments (like Docker or Airflow) running locally to auth via a browser URL.
|
|
@@ -82,6 +82,7 @@ boto =
|
|
|
82
82
|
development =
|
|
83
83
|
Cython
|
|
84
84
|
coverage
|
|
85
|
+
mitmproxy>=6.0.0; python_version >= '3.10'
|
|
85
86
|
more-itertools
|
|
86
87
|
numpy<=2.2.4
|
|
87
88
|
pendulum!=2.1.1
|
|
@@ -94,7 +95,8 @@ development =
|
|
|
94
95
|
pytzdata
|
|
95
96
|
responses
|
|
96
97
|
pandas =
|
|
97
|
-
pandas>=
|
|
98
|
+
pandas>=1.0.0,<3.0.0; python_version < '3.13'
|
|
99
|
+
pandas>=2.1.2,<3.0.0; python_version >= '3.13'
|
|
98
100
|
pyarrow>=14.0.1
|
|
99
101
|
secure-local-storage =
|
|
100
102
|
keyring>=23.1.0,<26.0.0
|
|
@@ -476,6 +476,10 @@ DEFAULT_CONFIGURATION: dict[str, tuple[Any, type | tuple[type, ...]]] = {
|
|
|
476
476
|
None,
|
|
477
477
|
(type(None), bool),
|
|
478
478
|
), # Run CRL cache cleanup in the background
|
|
479
|
+
"crl_download_max_size": (
|
|
480
|
+
None,
|
|
481
|
+
(type(None), int),
|
|
482
|
+
), # Maximum CRL file size in bytes
|
|
479
483
|
}
|
|
480
484
|
|
|
481
485
|
APPLICATION_RE = re.compile(r"[\w\d_]+")
|
|
@@ -786,6 +790,20 @@ class SnowflakeConnection:
|
|
|
786
790
|
return self._crl_cache_start_cleanup
|
|
787
791
|
return self._crl_config.crl_cache_start_cleanup
|
|
788
792
|
|
|
793
|
+
@property
|
|
794
|
+
def crl_download_max_size(self) -> int | None:
|
|
795
|
+
"""Maximum CRL file size in bytes."""
|
|
796
|
+
if not self._crl_config:
|
|
797
|
+
return self._crl_download_max_size
|
|
798
|
+
return self._crl_config.crl_download_max_size
|
|
799
|
+
|
|
800
|
+
@property
|
|
801
|
+
def skip_file_permissions_check(self) -> bool | None:
|
|
802
|
+
"""Whether to skip file permission checks for CRL cache files."""
|
|
803
|
+
if not self._crl_config:
|
|
804
|
+
return self._skip_file_permissions_check
|
|
805
|
+
return self._crl_config.skip_file_permissions_check
|
|
806
|
+
|
|
789
807
|
@property
|
|
790
808
|
def session_id(self) -> int:
|
|
791
809
|
return self._session_id
|
{snowflake_connector_python-4.1.0 → snowflake_connector_python-4.1.1}/src/snowflake/connector/crl.py
RENAMED
|
@@ -64,6 +64,8 @@ class CRLConfig:
|
|
|
64
64
|
crl_cache_removal_delay_days: int = 7
|
|
65
65
|
crl_cache_cleanup_interval_hours: int = 1
|
|
66
66
|
crl_cache_start_cleanup: bool = False
|
|
67
|
+
crl_download_max_size: int = 200 * 1024 * 1024 # 200 MB
|
|
68
|
+
unsafe_skip_file_permissions_check: bool = False
|
|
67
69
|
|
|
68
70
|
@classmethod
|
|
69
71
|
def from_connection(cls, sf_connection) -> CRLConfig:
|
|
@@ -158,6 +160,15 @@ class CRLConfig:
|
|
|
158
160
|
if sf_connection.crl_cache_start_cleanup is None
|
|
159
161
|
else bool(sf_connection.crl_cache_start_cleanup)
|
|
160
162
|
)
|
|
163
|
+
crl_download_max_size = (
|
|
164
|
+
cls.crl_download_max_size
|
|
165
|
+
if sf_connection.crl_download_max_size is None
|
|
166
|
+
else int(sf_connection.crl_download_max_size)
|
|
167
|
+
)
|
|
168
|
+
# Use the existing unsafe_skip_file_permissions_check flag from connection
|
|
169
|
+
unsafe_skip_file_permissions_check = bool(
|
|
170
|
+
sf_connection._unsafe_skip_file_permissions_check
|
|
171
|
+
)
|
|
161
172
|
|
|
162
173
|
return cls(
|
|
163
174
|
cert_revocation_check_mode=cert_revocation_check_mode,
|
|
@@ -171,6 +182,8 @@ class CRLConfig:
|
|
|
171
182
|
crl_cache_removal_delay_days=crl_cache_removal_delay_days,
|
|
172
183
|
crl_cache_cleanup_interval_hours=crl_cache_cleanup_interval_hours,
|
|
173
184
|
crl_cache_start_cleanup=crl_cache_start_cleanup,
|
|
185
|
+
crl_download_max_size=crl_download_max_size,
|
|
186
|
+
unsafe_skip_file_permissions_check=unsafe_skip_file_permissions_check,
|
|
174
187
|
)
|
|
175
188
|
|
|
176
189
|
|
|
@@ -185,6 +198,7 @@ class CRLValidator:
|
|
|
185
198
|
read_timeout_ms: int = CRLConfig.read_timeout_ms,
|
|
186
199
|
cache_validity_time: timedelta = CRLConfig.cache_validity_time,
|
|
187
200
|
cache_manager: CRLCacheManager | None = None,
|
|
201
|
+
crl_download_max_size: int = CRLConfig.crl_download_max_size,
|
|
188
202
|
):
|
|
189
203
|
self._session_manager = session_manager
|
|
190
204
|
self._cert_revocation_check_mode = cert_revocation_check_mode
|
|
@@ -193,6 +207,7 @@ class CRLValidator:
|
|
|
193
207
|
self._read_timeout_ms = read_timeout_ms
|
|
194
208
|
self._cache_validity_time = cache_validity_time
|
|
195
209
|
self._cache_manager = cache_manager or CRLCacheManager.noop()
|
|
210
|
+
self._crl_download_max_size = crl_download_max_size
|
|
196
211
|
|
|
197
212
|
# list of trusted CA and their certificates
|
|
198
213
|
self._trusted_ca: dict[x509.Name, list[x509.Certificate]] = defaultdict(list)
|
|
@@ -237,7 +252,9 @@ class CRLValidator:
|
|
|
237
252
|
if config.enable_crl_file_cache:
|
|
238
253
|
removal_delay = timedelta(days=config.crl_cache_removal_delay_days)
|
|
239
254
|
file_cache = CRLCacheFactory.get_file_cache(
|
|
240
|
-
cache_dir=config.crl_cache_dir,
|
|
255
|
+
cache_dir=config.crl_cache_dir,
|
|
256
|
+
removal_delay=removal_delay,
|
|
257
|
+
unsafe_skip_file_permissions_check=config.unsafe_skip_file_permissions_check,
|
|
241
258
|
)
|
|
242
259
|
else:
|
|
243
260
|
from snowflake.connector.crl_cache import NoopCRLCache
|
|
@@ -268,6 +285,7 @@ class CRLValidator:
|
|
|
268
285
|
read_timeout_ms=config.read_timeout_ms,
|
|
269
286
|
cache_validity_time=config.cache_validity_time,
|
|
270
287
|
cache_manager=cache_manager,
|
|
288
|
+
crl_download_max_size=config.crl_download_max_size,
|
|
271
289
|
)
|
|
272
290
|
|
|
273
291
|
def validate_certificate_chain(
|
|
@@ -550,15 +568,99 @@ class CRLValidator:
|
|
|
550
568
|
try:
|
|
551
569
|
logger.debug("Trying to download CRL from: %s", crl_url)
|
|
552
570
|
response = self._session_manager.get(
|
|
553
|
-
crl_url,
|
|
571
|
+
crl_url,
|
|
572
|
+
timeout=(self._connection_timeout_ms, self._read_timeout_ms),
|
|
573
|
+
stream=True,
|
|
554
574
|
)
|
|
555
575
|
response.raise_for_status()
|
|
556
|
-
|
|
576
|
+
|
|
577
|
+
# Check Content-Length header first if available
|
|
578
|
+
content_length = response.headers.get("Content-Length")
|
|
579
|
+
if content_length:
|
|
580
|
+
try:
|
|
581
|
+
size = int(content_length)
|
|
582
|
+
if size > self._crl_download_max_size:
|
|
583
|
+
logger.warning(
|
|
584
|
+
"CRL from %s exceeds maximum size limit (%d bytes > %d bytes)",
|
|
585
|
+
crl_url,
|
|
586
|
+
size,
|
|
587
|
+
self._crl_download_max_size,
|
|
588
|
+
)
|
|
589
|
+
return None
|
|
590
|
+
except ValueError:
|
|
591
|
+
logger.debug(
|
|
592
|
+
"Invalid Content-Length header for %s: %s",
|
|
593
|
+
crl_url,
|
|
594
|
+
content_length,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
# Stream the content and check size as we download
|
|
598
|
+
chunks = []
|
|
599
|
+
total_size = 0
|
|
600
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
601
|
+
if not chunk:
|
|
602
|
+
continue
|
|
603
|
+
total_size += len(chunk)
|
|
604
|
+
if total_size > self._crl_download_max_size:
|
|
605
|
+
logger.warning(
|
|
606
|
+
"CRL from %s exceeded maximum size limit during download (%d bytes)",
|
|
607
|
+
crl_url,
|
|
608
|
+
self._crl_download_max_size,
|
|
609
|
+
)
|
|
610
|
+
return None
|
|
611
|
+
chunks.append(chunk)
|
|
612
|
+
|
|
613
|
+
return b"".join(chunks)
|
|
557
614
|
except Exception:
|
|
558
615
|
# CRL fetch or parsing failed
|
|
559
616
|
logger.exception("Failed to download CRL from %s", crl_url)
|
|
560
617
|
return None
|
|
561
618
|
|
|
619
|
+
def _get_crl_last_update(
|
|
620
|
+
self, crl: x509.CertificateRevocationList
|
|
621
|
+
) -> datetime | None:
|
|
622
|
+
"""
|
|
623
|
+
Get the last_update timestamp from a CRL.
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
crl: The CRL to extract the timestamp from
|
|
627
|
+
|
|
628
|
+
Returns:
|
|
629
|
+
The last_update timestamp, or None if not available
|
|
630
|
+
"""
|
|
631
|
+
try:
|
|
632
|
+
return crl.last_update_utc
|
|
633
|
+
except AttributeError:
|
|
634
|
+
return getattr(crl, "last_update", None)
|
|
635
|
+
|
|
636
|
+
def _is_crl_more_recent(
|
|
637
|
+
self,
|
|
638
|
+
new_crl: x509.CertificateRevocationList,
|
|
639
|
+
cached_crl: x509.CertificateRevocationList,
|
|
640
|
+
) -> bool:
|
|
641
|
+
"""
|
|
642
|
+
Check if a newly downloaded CRL is more recent than a cached CRL.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
new_crl: The newly downloaded CRL
|
|
646
|
+
cached_crl: The cached CRL
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
True if new_crl is more recent (has a later last_update), False otherwise
|
|
650
|
+
"""
|
|
651
|
+
new_last_update = self._get_crl_last_update(new_crl)
|
|
652
|
+
cached_last_update = self._get_crl_last_update(cached_crl)
|
|
653
|
+
|
|
654
|
+
if new_last_update is None:
|
|
655
|
+
logger.warning("New CRL has no last_update timestamp")
|
|
656
|
+
return False
|
|
657
|
+
|
|
658
|
+
if cached_last_update is None:
|
|
659
|
+
logger.warning("Cached CRL has no last_update timestamp")
|
|
660
|
+
return True
|
|
661
|
+
|
|
662
|
+
return new_last_update > cached_last_update
|
|
663
|
+
|
|
562
664
|
def _download_crl(
|
|
563
665
|
self, crl_url: str
|
|
564
666
|
) -> tuple[x509.CertificateRevocationList | None, datetime | None]:
|
|
@@ -572,7 +674,12 @@ class CRLValidator:
|
|
|
572
674
|
except AttributeError:
|
|
573
675
|
next_update = crl.next_update
|
|
574
676
|
|
|
575
|
-
if
|
|
677
|
+
if not next_update:
|
|
678
|
+
# reject CRL as lack of next_update timestamp is a violation of both the RFC and the governing policy documents.
|
|
679
|
+
logger.warning("CRL from %s has no next_update timestamp", crl_url)
|
|
680
|
+
return None, None
|
|
681
|
+
|
|
682
|
+
if now > next_update:
|
|
576
683
|
logger.warning(
|
|
577
684
|
"The CRL from %s was expired on %s", crl_url, next_update
|
|
578
685
|
)
|
|
@@ -595,9 +702,27 @@ class CRLValidator:
|
|
|
595
702
|
or cached_crl.is_crl_expired_by(now)
|
|
596
703
|
or cached_crl.is_evicted_by(now, self._cache_validity_time)
|
|
597
704
|
):
|
|
705
|
+
logger.debug("Cached CRL is None/expired/evicted, downloading new CRL")
|
|
598
706
|
crl, ts = self._download_crl(crl_url)
|
|
599
|
-
if crl and ts:
|
|
600
|
-
|
|
707
|
+
if crl is not None and ts is not None:
|
|
708
|
+
# Only cache the downloaded CRL if it's more recent than the cached one
|
|
709
|
+
is_more_recent = cached_crl is None or self._is_crl_more_recent(
|
|
710
|
+
crl, cached_crl.crl
|
|
711
|
+
)
|
|
712
|
+
logger.debug(
|
|
713
|
+
"Is downloaded CRL more recent? cached_crl is None=%s, is_more_recent=%s",
|
|
714
|
+
cached_crl is None,
|
|
715
|
+
is_more_recent,
|
|
716
|
+
)
|
|
717
|
+
if is_more_recent:
|
|
718
|
+
self._put_crl_to_cache(crl_url, crl, ts)
|
|
719
|
+
logger.debug("Cached newly downloaded CRL for %s", crl_url)
|
|
720
|
+
else:
|
|
721
|
+
logger.info(
|
|
722
|
+
"Downloaded CRL for %s is not more recent than cached version, keeping cached CRL",
|
|
723
|
+
crl_url,
|
|
724
|
+
)
|
|
725
|
+
crl = cached_crl.crl
|
|
601
726
|
else:
|
|
602
727
|
crl = cached_crl.crl
|
|
603
728
|
|
|
@@ -6,9 +6,9 @@ import hashlib
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
import platform
|
|
9
|
+
import stat
|
|
9
10
|
import threading
|
|
10
11
|
from abc import ABC, abstractmethod
|
|
11
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
from datetime import datetime, timedelta, timezone
|
|
14
14
|
from pathlib import Path
|
|
@@ -18,6 +18,8 @@ from cryptography.hazmat.backends import default_backend
|
|
|
18
18
|
from cryptography.hazmat.primitives import serialization
|
|
19
19
|
from filelock import BaseFileLock, FileLock
|
|
20
20
|
|
|
21
|
+
from .compat import IS_WINDOWS
|
|
22
|
+
|
|
21
23
|
logger = logging.getLogger(__name__)
|
|
22
24
|
|
|
23
25
|
|
|
@@ -201,7 +203,10 @@ class CRLFileCache(CRLCache):
|
|
|
201
203
|
"""
|
|
202
204
|
|
|
203
205
|
def __init__(
|
|
204
|
-
self,
|
|
206
|
+
self,
|
|
207
|
+
cache_dir: Path | None = None,
|
|
208
|
+
removal_delay: timedelta | None = None,
|
|
209
|
+
unsafe_skip_file_permissions_check: bool = False,
|
|
205
210
|
):
|
|
206
211
|
"""
|
|
207
212
|
Initialize the file cache.
|
|
@@ -209,6 +214,7 @@ class CRLFileCache(CRLCache):
|
|
|
209
214
|
Args:
|
|
210
215
|
cache_dir: Directory to store cached CRLs
|
|
211
216
|
removal_delay: How long to wait before removing expired files
|
|
217
|
+
unsafe_skip_file_permissions_check: Skip file permission validation for security
|
|
212
218
|
|
|
213
219
|
Raises:
|
|
214
220
|
OSError: If cache directory cannot be created
|
|
@@ -216,14 +222,24 @@ class CRLFileCache(CRLCache):
|
|
|
216
222
|
self._cache_file_lock_timeout = 5.0
|
|
217
223
|
self._cache_dir = cache_dir or _get_default_crl_cache_path()
|
|
218
224
|
self._removal_delay = removal_delay or timedelta(days=7)
|
|
225
|
+
self._unsafe_skip_file_permissions_check = unsafe_skip_file_permissions_check
|
|
219
226
|
|
|
220
227
|
self._ensure_cache_directory_exists()
|
|
221
228
|
|
|
222
229
|
def _ensure_cache_directory_exists(self) -> None:
|
|
223
|
-
"""Create the cache directory if it doesn't exist."""
|
|
230
|
+
"""Create the cache directory if it doesn't exist with secure permissions."""
|
|
224
231
|
try:
|
|
225
|
-
|
|
232
|
+
# Create directory with secure permissions (owner read/write/execute only)
|
|
233
|
+
self._cache_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
234
|
+
|
|
235
|
+
# Verify directory permissions (if it already existed)
|
|
236
|
+
if not self._unsafe_skip_file_permissions_check:
|
|
237
|
+
self._check_permissions(self._cache_dir, "directory", "0o700")
|
|
238
|
+
|
|
226
239
|
logger.debug(f"Cache directory created/verified: {self._cache_dir}")
|
|
240
|
+
except PermissionError:
|
|
241
|
+
# Re-raise permission errors as-is
|
|
242
|
+
raise
|
|
227
243
|
except OSError as e:
|
|
228
244
|
raise OSError(f"Failed to create cache directory {self._cache_dir}: {e}")
|
|
229
245
|
|
|
@@ -249,6 +265,44 @@ class CRLFileCache(CRLCache):
|
|
|
249
265
|
timeout=self._cache_file_lock_timeout,
|
|
250
266
|
)
|
|
251
267
|
|
|
268
|
+
def _check_permissions(
|
|
269
|
+
self, path: Path, resource_type: str, expected_perms: str
|
|
270
|
+
) -> None:
|
|
271
|
+
"""
|
|
272
|
+
Check that a CRL cache resource has secure permissions (owner-only access).
|
|
273
|
+
|
|
274
|
+
Note: This check is only performed on Unix-like systems. Windows file
|
|
275
|
+
permissions work differently and are not checked.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
path: Path to the resource (file or directory) to check
|
|
279
|
+
resource_type: Description of the resource type (e.g., "file", "directory")
|
|
280
|
+
expected_perms: Description of expected permissions (e.g., "0o600 or 0o400", "0o700")
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
PermissionError: If resource permissions are too wide
|
|
284
|
+
"""
|
|
285
|
+
# Skip permission checks on Windows as they work differently
|
|
286
|
+
if IS_WINDOWS:
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
stat_info = path.stat()
|
|
291
|
+
actual_permissions = stat.S_IMODE(stat_info.st_mode)
|
|
292
|
+
|
|
293
|
+
# Check that resource is accessible only by owner (no group/other permissions)
|
|
294
|
+
if (
|
|
295
|
+
actual_permissions & 0o077 != 0
|
|
296
|
+
): # Check if group or others have any permission
|
|
297
|
+
raise PermissionError(
|
|
298
|
+
f"CRL cache {resource_type} {path} has insecure permissions: {oct(actual_permissions)}. "
|
|
299
|
+
f"{resource_type.capitalize()} must be accessible only by the owner ({expected_perms})."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
except FileNotFoundError:
|
|
303
|
+
# Resource doesn't exist yet, this is fine
|
|
304
|
+
pass
|
|
305
|
+
|
|
252
306
|
def get(self, crl_url: str) -> CRLCacheEntry | None:
|
|
253
307
|
"""
|
|
254
308
|
Get a CRL cache entry from disk.
|
|
@@ -265,6 +319,14 @@ class CRLFileCache(CRLCache):
|
|
|
265
319
|
if crl_file_path.exists():
|
|
266
320
|
logger.debug(f"Found CRL on disk for {crl_file_path}")
|
|
267
321
|
|
|
322
|
+
# Check file permissions before reading
|
|
323
|
+
if not self._unsafe_skip_file_permissions_check:
|
|
324
|
+
self._check_permissions(crl_file_path, "file", "0o600 or 0o400")
|
|
325
|
+
else:
|
|
326
|
+
logger.warning(
|
|
327
|
+
f"Skipping file permissions check for {crl_file_path}"
|
|
328
|
+
)
|
|
329
|
+
|
|
268
330
|
# Get file modification time as download time
|
|
269
331
|
stat_info = crl_file_path.stat()
|
|
270
332
|
download_time = datetime.fromtimestamp(
|
|
@@ -278,6 +340,11 @@ class CRLFileCache(CRLCache):
|
|
|
278
340
|
crl = x509.load_der_x509_crl(crl_data, backend=default_backend())
|
|
279
341
|
return CRLCacheEntry(crl, download_time)
|
|
280
342
|
|
|
343
|
+
except PermissionError as e:
|
|
344
|
+
logger.error(
|
|
345
|
+
f"Permission error reading CRL from disk cache for {crl_url}: {e}"
|
|
346
|
+
)
|
|
347
|
+
return None
|
|
281
348
|
except Exception as e:
|
|
282
349
|
logger.warning(f"Failed to read CRL from disk cache for {crl_url}: {e}")
|
|
283
350
|
|
|
@@ -297,9 +364,17 @@ class CRLFileCache(CRLCache):
|
|
|
297
364
|
# Serialize the CRL to DER format
|
|
298
365
|
crl_data = entry.crl.public_bytes(serialization.Encoding.DER)
|
|
299
366
|
|
|
300
|
-
# Write to file
|
|
301
|
-
|
|
302
|
-
|
|
367
|
+
# Write to file with secure permissions (owner read/write only)
|
|
368
|
+
# Using os.open with 0o600 ensures the file is created with secure permissions
|
|
369
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
370
|
+
if IS_WINDOWS:
|
|
371
|
+
# flag necessary for writing binary data to file on Windows OS
|
|
372
|
+
flags |= os.O_BINARY
|
|
373
|
+
fd = os.open(crl_file_path, flags, 0o600)
|
|
374
|
+
try:
|
|
375
|
+
os.write(fd, crl_data)
|
|
376
|
+
finally:
|
|
377
|
+
os.close(fd)
|
|
303
378
|
|
|
304
379
|
# Set file modification time to download time
|
|
305
380
|
download_timestamp = entry.download_time.timestamp()
|
|
@@ -426,7 +501,7 @@ class CRLCacheFactory:
|
|
|
426
501
|
_instance_lock = threading.RLock()
|
|
427
502
|
|
|
428
503
|
# Cleanup management
|
|
429
|
-
|
|
504
|
+
_cleanup_thread: threading.Thread | None = None
|
|
430
505
|
_cleanup_shutdown: threading.Event = threading.Event()
|
|
431
506
|
_cleanup_interval: timedelta | None = None
|
|
432
507
|
_atexit_registered: bool = False
|
|
@@ -454,7 +529,10 @@ class CRLCacheFactory:
|
|
|
454
529
|
|
|
455
530
|
@classmethod
|
|
456
531
|
def get_file_cache(
|
|
457
|
-
cls,
|
|
532
|
+
cls,
|
|
533
|
+
cache_dir: Path | None = None,
|
|
534
|
+
removal_delay: timedelta | None = None,
|
|
535
|
+
unsafe_skip_file_permissions_check: bool = False,
|
|
458
536
|
) -> CRLFileCache:
|
|
459
537
|
"""
|
|
460
538
|
Get or create a singleton CRLFileCache instance.
|
|
@@ -462,17 +540,23 @@ class CRLCacheFactory:
|
|
|
462
540
|
Args:
|
|
463
541
|
cache_dir: Directory to store cached CRLs
|
|
464
542
|
removal_delay: How long to wait before removing expired files
|
|
543
|
+
unsafe_skip_file_permissions_check: Skip file permission validation for security
|
|
465
544
|
|
|
466
545
|
Returns:
|
|
467
546
|
The singleton CRLFileCache instance
|
|
468
547
|
"""
|
|
469
548
|
with cls._instance_lock:
|
|
470
549
|
if cls._file_cache_instance is None:
|
|
471
|
-
cls._file_cache_instance = CRLFileCache(
|
|
550
|
+
cls._file_cache_instance = CRLFileCache(
|
|
551
|
+
cache_dir, removal_delay, unsafe_skip_file_permissions_check
|
|
552
|
+
)
|
|
472
553
|
else:
|
|
473
554
|
# Check if parameters differ from existing instance
|
|
474
555
|
existing_cache_dir = cls._file_cache_instance._cache_dir
|
|
475
556
|
existing_removal_delay = cls._file_cache_instance._removal_delay
|
|
557
|
+
existing_skip_check = (
|
|
558
|
+
cls._file_cache_instance._unsafe_skip_file_permissions_check
|
|
559
|
+
)
|
|
476
560
|
requested_cache_dir = cache_dir or _get_default_crl_cache_path()
|
|
477
561
|
requested_removal_delay = removal_delay or timedelta(days=7)
|
|
478
562
|
|
|
@@ -486,6 +570,11 @@ class CRLCacheFactory:
|
|
|
486
570
|
f"CRLs file cache has already been initialized with removal delay of {existing_removal_delay}, "
|
|
487
571
|
f"ignoring new removal delay of {requested_removal_delay}"
|
|
488
572
|
)
|
|
573
|
+
if existing_skip_check != unsafe_skip_file_permissions_check:
|
|
574
|
+
logger.warning(
|
|
575
|
+
f"CRLs file cache has already been initialized with unsafe_skip_file_permissions_check={existing_skip_check}, "
|
|
576
|
+
f"ignoring new value {unsafe_skip_file_permissions_check}"
|
|
577
|
+
)
|
|
489
578
|
return cls._file_cache_instance
|
|
490
579
|
|
|
491
580
|
@classmethod
|
|
@@ -504,8 +593,10 @@ class CRLCacheFactory:
|
|
|
504
593
|
cls.stop_periodic_cleanup()
|
|
505
594
|
|
|
506
595
|
cls._cleanup_interval = cleanup_interval
|
|
507
|
-
cls.
|
|
508
|
-
|
|
596
|
+
cls._cleanup_thread = threading.Thread(
|
|
597
|
+
target=cls._cleanup_loop,
|
|
598
|
+
name="crl-cache-cleanup",
|
|
599
|
+
daemon=True, # Make it a daemon thread so it doesn't block program exit
|
|
509
600
|
)
|
|
510
601
|
|
|
511
602
|
# Register atexit handler for graceful shutdown (only once)
|
|
@@ -513,8 +604,8 @@ class CRLCacheFactory:
|
|
|
513
604
|
atexit.register(cls._atexit_cleanup_handler)
|
|
514
605
|
cls._atexit_registered = True
|
|
515
606
|
|
|
516
|
-
#
|
|
517
|
-
cls.
|
|
607
|
+
# Start the cleanup thread
|
|
608
|
+
cls._cleanup_thread.start()
|
|
518
609
|
|
|
519
610
|
logger.debug(
|
|
520
611
|
f"Scheduled CRL cache cleanup task to run every {cleanup_interval.total_seconds()} seconds."
|
|
@@ -523,29 +614,29 @@ class CRLCacheFactory:
|
|
|
523
614
|
@classmethod
|
|
524
615
|
def stop_periodic_cleanup(cls) -> None:
|
|
525
616
|
"""Stop the periodic cleanup task."""
|
|
526
|
-
|
|
617
|
+
thread_to_join = None
|
|
527
618
|
|
|
528
619
|
with cls._instance_lock:
|
|
529
|
-
if cls.
|
|
620
|
+
if cls._cleanup_thread is None or cls._cleanup_shutdown.is_set():
|
|
530
621
|
return
|
|
531
622
|
|
|
532
623
|
cls._cleanup_shutdown.set()
|
|
533
|
-
|
|
624
|
+
thread_to_join = cls._cleanup_thread
|
|
534
625
|
|
|
535
|
-
#
|
|
536
|
-
if
|
|
537
|
-
|
|
626
|
+
# Join thread outside of lock to avoid deadlock
|
|
627
|
+
if thread_to_join is not None and thread_to_join.is_alive():
|
|
628
|
+
thread_to_join.join(timeout=5.0)
|
|
538
629
|
|
|
539
630
|
with cls._instance_lock:
|
|
540
631
|
cls._cleanup_shutdown.clear()
|
|
541
|
-
cls.
|
|
632
|
+
cls._cleanup_thread = None
|
|
542
633
|
cls._cleanup_interval = None
|
|
543
634
|
|
|
544
635
|
@classmethod
|
|
545
636
|
def is_periodic_cleanup_running(cls) -> bool:
|
|
546
637
|
"""Check if periodic cleanup task is running."""
|
|
547
638
|
with cls._instance_lock:
|
|
548
|
-
return cls.
|
|
639
|
+
return cls._cleanup_thread is not None and cls._cleanup_thread.is_alive()
|
|
549
640
|
|
|
550
641
|
@classmethod
|
|
551
642
|
def _cleanup_loop(cls) -> None:
|
|
@@ -1222,3 +1222,6 @@ class SnowflakeRestful:
|
|
|
1222
1222
|
|
|
1223
1223
|
def use_session(self, url: str | bytes) -> Generator[Session, Any, None]:
|
|
1224
1224
|
return self.session_manager.use_session(url)
|
|
1225
|
+
|
|
1226
|
+
def use_requests_session(self, url: str | bytes) -> Generator[Session, Any, None]:
|
|
1227
|
+
return self.use_session(url)
|
|
@@ -360,7 +360,7 @@ class ResultBatch(abc.ABC):
|
|
|
360
360
|
and connection.rest.session_manager is not None
|
|
361
361
|
):
|
|
362
362
|
# If connection was explicitly passed and not closed yet - we can reuse SessionManager with session pooling
|
|
363
|
-
with connection.rest.
|
|
363
|
+
with connection.rest.use_requests_session(
|
|
364
364
|
request_data["url"]
|
|
365
365
|
) as session:
|
|
366
366
|
logger.debug(
|
|
@@ -483,26 +483,32 @@ class SessionManager(_RequestVerbsUsingSessionMixin, _HttpConfigDirectAccessMixi
|
|
|
483
483
|
self._mount_adapters(session)
|
|
484
484
|
return session
|
|
485
485
|
|
|
486
|
+
@staticmethod
|
|
487
|
+
def _normalize_url(url: str | bytes | None) -> str:
|
|
488
|
+
"""Normalize URL to string format (handles bytes from storage client)."""
|
|
489
|
+
return url.decode("utf-8") if isinstance(url, bytes) else url
|
|
490
|
+
|
|
486
491
|
@contextlib.contextmanager
|
|
487
492
|
@_propagate_session_manager_to_ocsp
|
|
488
493
|
def use_session(
|
|
489
|
-
self, url: str | bytes, use_pooling: bool | None = None
|
|
494
|
+
self, url: str | bytes | None, use_pooling: bool | None = None
|
|
490
495
|
) -> Generator[Session, Any, None]:
|
|
496
|
+
"""Yield a session for the given URL (used for proxy handling and pooling).
|
|
497
|
+
The 'url' is an obligatory parameter due to the need for correct proxy handling (i.e. bypassing caused by no_proxy settings).
|
|
491
498
|
"""
|
|
492
|
-
|
|
493
|
-
"""
|
|
499
|
+
url_str = self._normalize_url(url)
|
|
494
500
|
use_pooling = use_pooling if use_pooling is not None else self.use_pooling
|
|
495
501
|
if not use_pooling:
|
|
496
|
-
session = self.make_session(url=
|
|
502
|
+
session = self.make_session(url=url_str)
|
|
497
503
|
try:
|
|
498
504
|
yield session
|
|
499
505
|
finally:
|
|
500
506
|
session.close()
|
|
501
507
|
else:
|
|
502
|
-
yield from self._yield_session_from_pool(
|
|
508
|
+
yield from self._yield_session_from_pool(url_str)
|
|
503
509
|
|
|
504
510
|
def _yield_session_from_pool(
|
|
505
|
-
self, url: str |
|
|
511
|
+
self, url: str | None
|
|
506
512
|
) -> Generator[SessionT, Any, None]:
|
|
507
513
|
hostname = self._get_pooling_key_from_url(url)
|
|
508
514
|
pool = self._sessions_map[hostname]
|
|
@@ -635,8 +641,8 @@ class SessionManagerFactory:
|
|
|
635
641
|
config: HttpConfig | None = None, **http_config_kwargs
|
|
636
642
|
) -> SessionManager:
|
|
637
643
|
has_param_proxies = (
|
|
638
|
-
|
|
639
|
-
)
|
|
644
|
+
config and config.proxy_host is not None
|
|
645
|
+
) or "proxies" in http_config_kwargs
|
|
640
646
|
if has_param_proxies:
|
|
641
647
|
return ProxySessionManager(config, **http_config_kwargs)
|
|
642
648
|
else:
|