Skip to content

Commit d821e18

Browse files
jaharkesbgilbert
authored andcommitted
Don't allow instantiating AbstractSlide and _OpenSlideMap base classes
Decorate methods with '@AbstractMethod' to prevent instantiation unless all abstract methods and properties are overridden. Signed-off-by: Jan Harkes <[email protected]> Signed-off-by: Benjamin Gilbert <[email protected]>
1 parent 32bb27c commit d821e18

File tree

1 file changed

+13
-1
lines changed

1 file changed

+13
-1
lines changed

openslide/__init__.py

Lines changed: 13 additions & 1 deletion
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,6 +161,7 @@ def read_region(
151161
size: (width, height) tuple giving the region size."""
152162
raise NotImplementedError
153163

164+
@abstractmethod
154165
def set_cache(self, cache: OpenSlideCache) -> None:
155166
"""Use the specified cache to store recently decoded slide tiles.
156167
@@ -299,6 +310,7 @@ def __len__(self) -> int:
299310
def __iter__(self) -> Iterator[str]:
300311
return iter(self._keys())
301312

313+
@abstractmethod
302314
def _keys(self) -> list[str]:
303315
# Private method; always returns list.
304316
raise NotImplementedError()

0 commit comments

Comments
 (0)