Skip to content

Commit bbbc0eb

Browse files
authored
Merge pull request #294 from bgilbert/fixes
Small fixes
2 parents baf1c54 + 7d1be91 commit bbbc0eb

File tree

2 files changed

+18
-14
lines changed

2 files changed

+18
-14
lines changed

openslide/__init__.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
from __future__ import annotations
2727

28+
from abc import ABCMeta, abstractmethod
2829
from io import BytesIO
2930
from types import TracebackType
3031
from typing import Iterator, Literal, Mapping, TypeVar
@@ -62,7 +63,7 @@
6263
_T = TypeVar('_T')
6364

6465

65-
class AbstractSlide:
66+
class AbstractSlide(metaclass=ABCMeta):
6667
"""The base class of a slide object."""
6768

6869
def __init__(self) -> None:
@@ -81,22 +82,26 @@ def __exit__(
8182
return False
8283

8384
@classmethod
85+
@abstractmethod
8486
def detect_format(cls, filename: lowlevel.Filename) -> str | None:
8587
"""Return a string describing the format of the specified file.
8688
8789
If the file format is not recognized, return None."""
8890
raise NotImplementedError
8991

92+
@abstractmethod
9093
def close(self) -> None:
9194
"""Close the slide."""
9295
raise NotImplementedError
9396

9497
@property
98+
@abstractmethod
9599
def level_count(self) -> int:
96100
"""The number of levels in the image."""
97101
raise NotImplementedError
98102

99103
@property
104+
@abstractmethod
100105
def level_dimensions(self) -> tuple[tuple[int, int], ...]:
101106
"""A tuple of (width, height) tuples, one for each level of the image.
102107
@@ -109,20 +114,23 @@ def dimensions(self) -> tuple[int, int]:
109114
return self.level_dimensions[0]
110115

111116
@property
117+
@abstractmethod
112118
def level_downsamples(self) -> tuple[float, ...]:
113119
"""A tuple of downsampling factors for each level of the image.
114120
115121
level_downsample[n] contains the downsample factor of level n."""
116122
raise NotImplementedError
117123

118124
@property
125+
@abstractmethod
119126
def properties(self) -> Mapping[str, str]:
120127
"""Metadata about the image.
121128
122129
This is a map: property name -> property value."""
123130
raise NotImplementedError
124131

125132
@property
133+
@abstractmethod
126134
def associated_images(self) -> Mapping[str, Image.Image]:
127135
"""Images associated with this whole-slide image.
128136
@@ -136,10 +144,12 @@ def color_profile(self) -> ImageCms.ImageCmsProfile | None:
136144
return None
137145
return ImageCms.getOpenProfile(BytesIO(self._profile))
138146

147+
@abstractmethod
139148
def get_best_level_for_downsample(self, downsample: float) -> int:
140149
"""Return the best level for displaying the given downsample."""
141150
raise NotImplementedError
142151

152+
@abstractmethod
143153
def read_region(
144154
self, location: tuple[int, int], level: int, size: tuple[int, int]
145155
) -> Image.Image:
@@ -151,11 +161,13 @@ def read_region(
151161
size: (width, height) tuple giving the region size."""
152162
raise NotImplementedError
153163

154-
def set_cache(self, cache: OpenSlideCache) -> None:
164+
def set_cache(self, cache: OpenSlideCache) -> None: # noqa: B027
155165
"""Use the specified cache to store recently decoded slide tiles.
156166
167+
This class does not support caching, so this method does nothing.
168+
157169
cache: an OpenSlideCache object."""
158-
raise NotImplementedError
170+
pass
159171

160172
def get_thumbnail(self, size: tuple[int, int]) -> Image.Image:
161173
"""Return a PIL.Image containing an RGB thumbnail of the image.
@@ -299,6 +311,7 @@ def __len__(self) -> int:
299311
def __iter__(self) -> Iterator[str]:
300312
return iter(self._keys())
301313

314+
@abstractmethod
302315
def _keys(self) -> list[str]:
303316
# Private method; always returns list.
304317
raise NotImplementedError()
@@ -406,7 +419,7 @@ def level_dimensions(self) -> tuple[tuple[int, int]]:
406419
407420
level_dimensions[n] contains the dimensions of level n."""
408421
if self._image is None:
409-
raise ValueError('Passing closed slide object')
422+
raise ValueError('Cannot read from a closed slide')
410423
return (self._image.size,)
411424

412425
@property
@@ -444,7 +457,7 @@ def read_region(
444457
level: the level number.
445458
size: (width, height) tuple giving the region size."""
446459
if self._image is None:
447-
raise ValueError('Passing closed slide object')
460+
raise ValueError('Cannot read from a closed slide')
448461
if level != 0:
449462
raise OpenSlideError("Invalid level")
450463
if ['fail' for s in size if s < 0]:
@@ -474,14 +487,6 @@ def read_region(
474487
tile.info['icc_profile'] = self._profile
475488
return tile
476489

477-
def set_cache(self, cache: OpenSlideCache) -> None:
478-
"""Use the specified cache to store recently decoded slide tiles.
479-
480-
ImageSlide does not support caching, so this method does nothing.
481-
482-
cache: an OpenSlideCache object."""
483-
pass
484-
485490

486491
def open_slide(filename: lowlevel.Filename) -> OpenSlide | ImageSlide:
487492
"""Open a whole-slide or regular image.

openslide/lowlevel.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,6 @@ def read_associated_image_icc_profile(
568568
'openslide_set_cache',
569569
None,
570570
[_OpenSlide, _OpenSlideCache],
571-
None,
572571
minimum_version='4.0.0',
573572
)
574573

0 commit comments

Comments
 (0)