1414
1515from __future__ import annotations
1616
17+ import atexit
1718import os
1819from abc import ABC , abstractmethod
1920from pathlib import Path
20- from typing import TYPE_CHECKING , Any , Dict , Optional
21+ from typing import TYPE_CHECKING , Any , Dict , Final , Optional
2122
2223if TYPE_CHECKING :
24+ from snowflake .cli .api .config_ng .core import ValueSource
2325 from snowflake .cli .api .config_ng .resolver import ConfigurationResolver
2426
25- ALTERNATIVE_CONFIG_ENV_VAR = "SNOWFLAKE_CLI_CONFIG_V2_ENABLED"
27+ ALTERNATIVE_CONFIG_ENV_VAR : Final [ str ] = "SNOWFLAKE_CLI_CONFIG_V2_ENABLED"
2628
2729
2830class ConfigProvider (ABC ):
@@ -97,7 +99,6 @@ def _transform_private_key_raw(self, connection_dict: dict) -> dict:
9799 if "private_key_file" in connection_dict :
98100 return connection_dict
99101
100- import os
101102 import tempfile
102103
103104 try :
@@ -116,13 +117,35 @@ def _transform_private_key_raw(self, connection_dict: dict) -> dict:
116117 result ["private_key_file" ] = temp_file_path
117118 del result ["private_key_raw" ]
118119
120+ # Track created temp file on the provider instance for cleanup
121+ temp_files_attr = "_temp_private_key_files"
122+ existing = getattr (self , temp_files_attr , None )
123+ if existing is None :
124+ setattr (self , temp_files_attr , {temp_file_path })
125+ else :
126+ existing .add (temp_file_path )
127+
119128 return result
120129
121130 except Exception :
122131 # If transformation fails, return original dict
123132 # The error will be handled downstream
124133 return connection_dict
125134
135+ def cleanup_temp_files (self ) -> None :
136+ """Delete any temporary files created from private_key_raw transformation."""
137+ temp_files = getattr (self , "_temp_private_key_files" , None )
138+ if not temp_files :
139+ return
140+ to_remove = list (temp_files )
141+ for path in to_remove :
142+ try :
143+ Path (path ).unlink (missing_ok = True )
144+ except Exception :
145+ # Best-effort cleanup; ignore failures
146+ pass
147+ temp_files .clear ()
148+
126149
127150class LegacyConfigProvider (ConfigProvider ):
128151 """
@@ -409,7 +432,7 @@ def section_exists(self, *path) -> bool:
409432 )
410433
411434 # Source priority levels (higher number = higher priority)
412- _SOURCE_PRIORITIES = {
435+ _SOURCE_PRIORITIES : Final [ dict [ "ValueSource.SourceName" , int ]] = {
413436 "snowsql_config" : 1 ,
414437 "cli_config_toml" : 2 ,
415438 "connections_toml" : 3 ,
@@ -683,4 +706,23 @@ def reset_config_provider():
683706 Useful for testing and when config source changes.
684707 """
685708 global _config_provider_instance
709+ # Cleanup any temp files created by the current provider instance
710+ if _config_provider_instance is not None :
711+ try :
712+ _config_provider_instance .cleanup_temp_files ()
713+ except Exception :
714+ pass
686715 _config_provider_instance = None
716+
717+
718+ def _cleanup_provider_at_exit () -> None :
719+ """Process-exit cleanup for provider-managed temporary files."""
720+ global _config_provider_instance
721+ if _config_provider_instance is not None :
722+ try :
723+ _config_provider_instance .cleanup_temp_files ()
724+ except Exception :
725+ pass
726+
727+
728+ atexit .register (_cleanup_provider_at_exit )
0 commit comments