Skip to content

Commit cfd1663

Browse files
committed
Explicitly check for closed ImageSlide
When an operation is performed on a closed ImageSlide, we've historically raised an AttributeError upon dereferencing None. Add proper checks so the type system understands what we're doing, raising ValueError as lowlevel does. This is not an API change because it only affects invalid caller behavior. Signed-off-by: Benjamin Gilbert <[email protected]>
1 parent dccce62 commit cfd1663

File tree

2 files changed

+11
-7
lines changed

2 files changed

+11
-7
lines changed

openslide/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def __init__(self, file: str | Path | Image.Image):
366366
self._file_arg = file
367367
if isinstance(file, Image.Image):
368368
self._close = False
369-
self._image = file
369+
self._image: Image.Image | None = file
370370
else:
371371
self._close = True
372372
self._image = Image.open(file)
@@ -391,10 +391,10 @@ def detect_format(cls, filename: str | Path) -> str | None:
391391
def close(self) -> None:
392392
"""Close the slide object."""
393393
if self._close:
394+
assert self._image is not None
394395
self._image.close()
395396
self._close = False
396-
# is it necessary to set to None?
397-
self._image = None # type: ignore
397+
self._image = None
398398

399399
@property
400400
def level_count(self) -> Literal[1]:
@@ -406,6 +406,8 @@ def level_dimensions(self) -> tuple[tuple[int, int], ...]:
406406
"""A list of (width, height) tuples, one for each level of the image.
407407
408408
level_dimensions[n] contains the dimensions of level n."""
409+
if self._image is None:
410+
raise ValueError('Passing closed slide object')
409411
return (self._image.size,)
410412

411413
@property
@@ -442,6 +444,8 @@ def read_region(
442444
reference frame.
443445
level: the level number.
444446
size: (width, height) tuple giving the region size."""
447+
if self._image is None:
448+
raise ValueError('Passing closed slide object')
445449
if level != 0:
446450
raise OpenSlideError("Invalid level")
447451
if ['fail' for s in size if s < 0]:

tests/test_imageslide.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ def test_operations_on_closed_handle(self):
4949
osr = ImageSlide(img)
5050
osr.close()
5151
self.assertRaises(
52-
AttributeError, lambda: osr.read_region((0, 0), 0, (100, 100))
52+
ValueError, lambda: osr.read_region((0, 0), 0, (100, 100))
5353
)
54+
self.assertRaises(ValueError, lambda: osr.level_dimensions)
5455
# If an Image is passed to the constructor, ImageSlide.close()
5556
# shouldn't close it
5657
self.assertEqual(img.getpixel((0, 0)), 3)
@@ -59,9 +60,8 @@ def test_context_manager(self):
5960
osr = ImageSlide(file_path('boxes.png'))
6061
with osr:
6162
pass
62-
self.assertRaises(
63-
AttributeError, lambda: osr.read_region((0, 0), 0, (100, 100))
64-
)
63+
self.assertRaises(ValueError, lambda: osr.read_region((0, 0), 0, (100, 100)))
64+
self.assertRaises(ValueError, lambda: osr.level_dimensions)
6565

6666

6767
class _SlideTest:

0 commit comments

Comments
 (0)