From 343d4356a3878c798fe70a279f1d6b220ac483cc Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sun, 27 Oct 2024 14:31:01 -0700 Subject: [PATCH 1/2] Use explicit exception chaining when converting exception types This changes Python's exception chain reporting to make it clear that the new exception was not an unrelated failure while handling the original one, but rather the direct cause of it. Signed-off-by: Benjamin Gilbert --- openslide/__init__.py | 4 ++-- openslide/lowlevel.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openslide/__init__.py b/openslide/__init__.py index 60a32b41..39b83fa9 100644 --- a/openslide/__init__.py +++ b/openslide/__init__.py @@ -293,8 +293,8 @@ def set_cache(self, cache: OpenSlideCache) -> None: cache: an OpenSlideCache object.""" try: llcache = cache._openslide_cache - except AttributeError: - raise TypeError('Not a cache object') + except AttributeError as exc: + raise TypeError('Not a cache object') from exc lowlevel.set_cache(self._osr, llcache) diff --git a/openslide/lowlevel.py b/openslide/lowlevel.py index c24ca7c9..7adb0dbe 100644 --- a/openslide/lowlevel.py +++ b/openslide/lowlevel.py @@ -84,18 +84,18 @@ def try_load(names: list[str]) -> CDLL: if platform.system() == 'Windows': try: return try_load(['libopenslide-1.dll', 'libopenslide-0.dll']) - except FileNotFoundError: + except FileNotFoundError as exc: raise ModuleNotFoundError( "Couldn't locate OpenSlide DLL. " "Try `pip install openslide-bin`, " "or if you're using an OpenSlide binary package, " "ensure you've called os.add_dll_directory(). " "https://openslide.org/api/python/#installing" - ) + ) from exc elif platform.system() == 'Darwin': try: return try_load(['libopenslide.1.dylib', 'libopenslide.0.dylib']) - except OSError: + except OSError as exc: # MacPorts doesn't add itself to the dyld search path, but # does add itself to the find_library() search path # (DEFAULT_LIBRARY_FALLBACK in ctypes.macholib.dyld). @@ -107,17 +107,17 @@ def try_load(names: list[str]) -> CDLL: "Couldn't locate OpenSlide dylib. " "Try `pip install openslide-bin`. " "https://openslide.org/api/python/#installing" - ) + ) from exc return cdll.LoadLibrary(lib) else: try: return try_load(['libopenslide.so.1', 'libopenslide.so.0']) - except OSError: + except OSError as exc: raise ModuleNotFoundError( "Couldn't locate OpenSlide shared library. " "Try `pip install openslide-bin`. " "https://openslide.org/api/python/#installing" - ) + ) from exc _lib = _load_library() From d6fb5c09f0fc887ef1172e6cdefd9c7b9e58bf57 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sun, 27 Oct 2024 14:39:02 -0700 Subject: [PATCH 2/2] Include all LoadLibrary() failures in exception chain If libopenslide.so.1 fails to load, we want to report that exception too, not just the fact that libopenslide.so.0 doesn't exist. Make try_load() recursive rather than iterative, allowing Python's exception chaining to handle this automatically. Reported-by: Govinda Kamath Signed-off-by: Benjamin Gilbert --- openslide/lowlevel.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openslide/lowlevel.py b/openslide/lowlevel.py index 7adb0dbe..11ccc575 100644 --- a/openslide/lowlevel.py +++ b/openslide/lowlevel.py @@ -72,14 +72,16 @@ def _load_library() -> CDLL: pass def try_load(names: list[str]) -> CDLL: - for name in names: - try: - return cdll.LoadLibrary(name) - except OSError: - if name == names[-1]: - raise - else: - raise ValueError('No library names specified') + try: + return cdll.LoadLibrary(names[0]) + except OSError: + remaining = names[1:] + if remaining: + # handle recursively so implicit exception chaining captures + # all the failures + return try_load(remaining) + else: + raise if platform.system() == 'Windows': try: