Skip to content

Commit dd24813

Browse files
authored
fix: Update library loading code to improve error messages (#188)
1 parent c5bccf7 commit dd24813

File tree

1 file changed

+41
-11
lines changed

1 file changed

+41
-11
lines changed

src/c2pa/lib.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def _get_platform_dir() -> str:
106106

107107

108108
def _load_single_library(lib_name: str,
109-
search_paths: list[Path]) -> Optional[ctypes.CDLL]:
109+
search_paths: list[Path]) -> tuple[Optional[ctypes.CDLL], list[tuple[Path, Exception]]]:
110110
"""
111111
Load a single library from the given search paths.
112112
@@ -115,14 +115,16 @@ def _load_single_library(lib_name: str,
115115
search_paths: List of paths to search for the library
116116
117117
Returns:
118-
The loaded library or None if loading failed
118+
A tuple of (loaded library or None, list of (path, error) for files that were found but failed to load)
119119
"""
120120
if DEBUG_LIBRARY_LOADING: # pragma: no cover
121121
logger.info(f"Searching for library '{lib_name}' in paths: {[str(p) for p in search_paths]}")
122122
current_arch = _get_architecture()
123123
if DEBUG_LIBRARY_LOADING: # pragma: no cover
124124
logger.info(f"Current architecture: {current_arch}")
125125

126+
load_errors = []
127+
126128
for path in search_paths:
127129
lib_path = path / lib_name
128130
if DEBUG_LIBRARY_LOADING: # pragma: no cover
@@ -131,17 +133,20 @@ def _load_single_library(lib_name: str,
131133
if DEBUG_LIBRARY_LOADING: # pragma: no cover
132134
logger.info(f"Found library at: {lib_path}")
133135
try:
134-
return ctypes.CDLL(str(lib_path))
136+
return ctypes.CDLL(str(lib_path)), []
135137
except Exception as e:
136138
error_msg = str(e)
139+
load_errors.append((lib_path, e))
137140
if "incompatible architecture" in error_msg:
138141
logger.error(f"Architecture mismatch: Library at {lib_path} is not compatible with current architecture {current_arch}")
139142
logger.error(f"Error details: {error_msg}")
143+
elif "GLIBC" in error_msg or "version" in error_msg.lower():
144+
logger.error(f"Library dependency error at {lib_path}: {error_msg}")
140145
else:
141146
logger.error(f"Failed to load library from {lib_path}: {e}")
142147
else:
143148
logger.debug(f"Library not found at: {lib_path}")
144-
return None
149+
return None, load_errors
145150

146151

147152
def _get_possible_search_paths() -> list[Path]:
@@ -250,19 +255,44 @@ def dynamically_load_library(
250255

251256
if lib_name:
252257
# If specific library name is provided, only load that one
253-
lib = _load_single_library(lib_name, possible_paths)
258+
lib, load_errors = _load_single_library(lib_name, possible_paths)
254259
if not lib:
255260
platform_id = get_platform_identifier()
256261
current_arch = _get_architecture()
257-
logger.error(f"Could not find {lib_name} in any of the search paths: {[str(p) for p in possible_paths]}")
258-
logger.error(f"Platform: {platform_id}, Architecture: {current_arch}")
259-
raise RuntimeError(f"Could not find {lib_name} in any of the search paths (Platform: {platform_id}, Architecture: {current_arch})")
262+
263+
if load_errors:
264+
# Library files were found but failed to load
265+
error_details = "\n".join([f" - {path}: {error}" for path, error in load_errors])
266+
logger.error(f"Found {lib_name} but failed to load it. Errors encountered:")
267+
logger.error(error_details)
268+
logger.error(f"Platform: {platform_id}, Architecture: {current_arch}")
269+
raise RuntimeError(
270+
f"Found {lib_name} at {len(load_errors)} location(s) but failed to load:\n{error_details}\n"
271+
f"Platform: {platform_id}, Architecture: {current_arch}"
272+
)
273+
else:
274+
# Library file was not found in any search path
275+
logger.error(f"Could not find {lib_name} in any of the search paths: {[str(p) for p in possible_paths]}")
276+
logger.error(f"Platform: {platform_id}, Architecture: {current_arch}")
277+
raise RuntimeError(
278+
f"Could not find {lib_name} in any of the search paths (Platform: {platform_id}, Architecture: {current_arch})"
279+
)
260280
return lib
261281

262282
# Default path (no library name provided in the environment)
263-
c2pa_lib = _load_single_library(c2pa_lib_name, possible_paths)
283+
c2pa_lib, load_errors = _load_single_library(c2pa_lib_name, possible_paths)
264284
if not c2pa_lib:
265-
logger.error(f"Could not find {c2pa_lib_name} in any of the search paths: {[str(p) for p in possible_paths]}")
266-
raise RuntimeError(f"Could not find {c2pa_lib_name} in any of the search paths")
285+
if load_errors:
286+
# Library files were found but failed to load
287+
error_details = "\n".join([f" - {path}: {error}" for path, error in load_errors])
288+
logger.error(f"Found {c2pa_lib_name} but failed to load it. Errors encountered:")
289+
logger.error(error_details)
290+
raise RuntimeError(
291+
f"Found {c2pa_lib_name} at {len(load_errors)} location(s) but failed to load:\n{error_details}"
292+
)
293+
else:
294+
# Library file was not found in any search path
295+
logger.error(f"Could not find {c2pa_lib_name} in any of the search paths: {[str(p) for p in possible_paths]}")
296+
raise RuntimeError(f"Could not find {c2pa_lib_name} in any of the search paths")
267297

268298
return c2pa_lib

0 commit comments

Comments
 (0)