From 0100525f111ac0833f5c3047121349de2c3a54d5 Mon Sep 17 00:00:00 2001 From: monozoide Date: Thu, 16 Oct 2025 21:19:32 +0200 Subject: [PATCH] Fix Python 3.13 compatibility with pathlib #61 Refactored the SQL import/export functionality to use `importlib.resources.as_file` instead of the deprecated `pathlib.Path` context manager. This resolves a crash on Python 3.13, where `pathlib.Path` objects no longer support the context manager protocol. --- lib/maillogsentinel/sql_exporter.py | 21 +++++++++++++++------ lib/maillogsentinel/sql_importer.py | 15 +++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/maillogsentinel/sql_exporter.py b/lib/maillogsentinel/sql_exporter.py index f6812f2..4340900 100644 --- a/lib/maillogsentinel/sql_exporter.py +++ b/lib/maillogsentinel/sql_exporter.py @@ -396,9 +396,17 @@ def run_sql_export(config: AppConfig, output_log_level: str = "INFO") -> bool: f"{LOG_PREFIX}: No user-specific column mapping file configured, attempting to load bundled default." ) try: - with importlib.resources.files("lib.maillogsentinel.data").joinpath( - "maillogsentinel_sql_column_mapping.json" - ) as bundled_path: + # Correctly handle importlib.resources for Python 3.9+ + # The 'with' statement for pathlib.Path is removed in Python 3.13 + # We get a Traversable object, which we can convert to a Path + bundled_path_traversable = importlib.resources.files( + "lib.maillogsentinel.data" + ).joinpath("maillogsentinel_sql_column_mapping.json") + + # For older importlib_resources, we might need to use 'as_file' context manager + # but for modern Python, this direct conversion to Path is often sufficient + # if the resource is a file on the filesystem. + with importlib.resources.as_file(bundled_path_traversable) as bundled_path: if ( not bundled_path.is_file() ): # Should not happen if packaged correctly @@ -711,9 +719,10 @@ def _create_dummy_csv( def _get_bundled_mapping_headers_for_test(): try: - with importlib.resources.files("lib.maillogsentinel.data").joinpath( - "maillogsentinel_sql_column_mapping.json" - ) as bundled_path_ref: + bundled_path_traversable = importlib.resources.files( + "lib.maillogsentinel.data" + ).joinpath("maillogsentinel_sql_column_mapping.json") + with importlib.resources.as_file(bundled_path_traversable) as bundled_path_ref: # The object returned by importlib.resources.files() is a Traversable # We need to ensure it's treated as a Path object for load_column_mapping mapping = load_column_mapping(Path(bundled_path_ref)) diff --git a/lib/maillogsentinel/sql_importer.py b/lib/maillogsentinel/sql_importer.py index d61192a..1f6d3ae 100644 --- a/lib/maillogsentinel/sql_importer.py +++ b/lib/maillogsentinel/sql_importer.py @@ -331,12 +331,15 @@ def run_sql_import(config: AppConfig, output_log_level: str = "INFO") -> bool: f"{LOG_PREFIX}: No user-specific column mapping file configured, attempting to load bundled default." ) try: - with importlib.resources.files("lib.maillogsentinel.data").joinpath( - "maillogsentinel_sql_column_mapping.json" - ) as bundled_path_traversable: - final_mapping_file_path = Path( - bundled_path_traversable - ) # Convert Traversable to Path + bundled_path_traversable = importlib.resources.files( + "lib.maillogsentinel.data" + ).joinpath("maillogsentinel_sql_column_mapping.json") + with importlib.resources.as_file( + bundled_path_traversable + ) as resolved_bundled_path: + final_mapping_file_path = ( + resolved_bundled_path # This is now a concrete Path + ) if not final_mapping_file_path.is_file(): logger.critical( f"{LOG_PREFIX}: Bundled column mapping file not found at expected location via importlib.resources: {final_mapping_file_path}. This indicates a packaging issue. Aborting SQL import."