Skip to content

Commit 60f585a

Browse files
authored
Convert abstract base class to protocols (#267)
* Rename testing -> tests to prevent namespace collision with anndadta * Convert somacore abstract base classes to protocols * Run linters * Add feedback from PR * Run formatter
1 parent 579466c commit 60f585a

15 files changed

Lines changed: 373 additions & 1399 deletions

File tree

python-spec/src/somacore/__init__.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@
1616

1717
from .base import SOMAObject
1818
from .collection import Collection
19-
from .coordinates import AffineTransform
2019
from .coordinates import Axis
2120
from .coordinates import CoordinateSpace
2221
from .coordinates import CoordinateTransform
23-
from .coordinates import IdentityTransform
24-
from .coordinates import ScaleTransform
25-
from .coordinates import UniformScaleTransform
2622
from .data import DataFrame
2723
from .data import DenseNDArray
2824
from .data import NDArray
@@ -32,7 +28,6 @@
3228
from .experiment import Experiment
3329
from .measurement import Measurement
3430
from .options import BatchSize
35-
from .options import IOfN
3631
from .options import ResultOrder
3732
from .query import AxisColumnNames
3833
from .query import AxisQuery
@@ -68,7 +63,6 @@
6863
"Experiment",
6964
"Measurement",
7065
"Scene",
71-
"ImageProperties",
7266
"MultiscaleImage",
7367
"GeometryDataFrame",
7468
"PointCloudDataFrame",
@@ -82,8 +76,4 @@
8276
"Axis",
8377
"CoordinateSpace",
8478
"CoordinateTransform",
85-
"AffineTransform",
86-
"ScaleTransform",
87-
"UniformScaleTransform",
88-
"IdentityTransform",
8979
)

python-spec/src/somacore/_mixin.py

Lines changed: 0 additions & 100 deletions
This file was deleted.

python-spec/src/somacore/base.py

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,19 @@
66

77
from __future__ import annotations
88

9-
import abc
10-
from typing import Any, ClassVar, MutableMapping
9+
from typing import Any, MutableMapping, runtime_checkable
1110

12-
from typing_extensions import LiteralString, Self
11+
from typing_extensions import Protocol, Self
1312

1413
from . import options
1514
from . import types
1615

1716

18-
class SOMAObject(metaclass=abc.ABCMeta):
17+
@runtime_checkable
18+
class SOMAObject(Protocol):
1919
"""The base type for all SOMA objects, containing common behaviors."""
2020

21-
__slots__ = ("__weakref__",)
22-
2321
@classmethod
24-
@abc.abstractmethod
2522
def open(
2623
cls,
2724
uri: str,
@@ -41,10 +38,9 @@ def open(
4138
Returns: The SOMA object, opened for reading.
4239
Lifecycle: maturing
4340
"""
44-
raise NotImplementedError()
41+
...
4542

4643
@classmethod
47-
@abc.abstractmethod
4844
def exists(cls, uri: str, *, context: Any | None = None) -> bool:
4945
"""Checks whether a SOMA object of this type is stored at the URI.
5046
@@ -56,16 +52,15 @@ def exists(cls, uri: str, *, context: Any | None = None) -> bool:
5652
False if the object does not exist, or is of a different type.
5753
Lifecycle: maturing
5854
"""
59-
raise NotImplementedError()
55+
...
6056

6157
@property
62-
@abc.abstractmethod
6358
def uri(self) -> str:
6459
"""The URI of this SOMA object.
6560
6661
Lifecycle: maturing
6762
"""
68-
raise NotImplementedError()
63+
...
6964

7065
@property
7166
def context(self) -> types.ContextBase | None:
@@ -81,10 +76,9 @@ def context(self) -> types.ContextBase | None:
8176
8277
Lifecycle: maturing
8378
"""
84-
return None
79+
...
8580

8681
@property
87-
@abc.abstractmethod
8882
def metadata(self) -> MutableMapping[str, Any]:
8983
"""The metadata of this SOMA object.
9084
@@ -94,35 +88,23 @@ def metadata(self) -> MutableMapping[str, Any]:
9488
9589
Lifecycle: maturing
9690
"""
97-
raise NotImplementedError()
91+
...
9892

9993
@property
100-
@abc.abstractmethod
10194
def mode(self) -> options.OpenMode:
10295
"""Returns the mode this object was opened in, either ``r`` or ``w``.
10396
10497
Lifecycle: maturing
10598
"""
106-
raise NotImplementedError()
99+
...
107100

108101
@property
109-
@abc.abstractmethod
110102
def closed(self) -> bool:
111103
"""True if this object has been closed; False if still open.
112104
113105
Lifecycle: maturing
114106
"""
115-
raise NotImplementedError()
116-
117-
soma_type: ClassVar[LiteralString]
118-
"""A string describing the SOMA type of this object. This is constant.
119-
120-
This uses ClassVar since you can't do abstract class properties.
121-
This is the equivalent, just without abc-based automatic verification.
122-
Overrides are marked Final with an ignore[misc] because mypy by default
123-
wants this to be mutable, and doesn't like overriding the mutable member
124-
with a Final member.
125-
"""
107+
...
126108

127109
# Context management
128110

@@ -138,28 +120,8 @@ def close(self) -> None:
138120
139121
Lifecycle: maturing
140122
"""
141-
# Default implementation does nothing.
142-
143-
def __enter__(self) -> Self:
144-
return self
145-
146-
def __exit__(self, *_: Any) -> None:
147-
self.close()
148-
149-
def __del__(self) -> None:
150-
self.close()
151-
super_del = getattr(super(), "__del__", lambda: None)
152-
super_del()
153-
154-
# Explicitly use Python's identity-based equality/hash checks.
155-
# These will show up in the `__mro__` before any other classes
156-
# provided a SOMAObject base is put first:
157-
#
158-
# class SubType(SomeSOMAObject, MutableMapping):
159-
# ...
160-
#
161-
# # sub_type_inst.__eq__ uses object.__eq__ rather than
162-
# # MutableMapping.__eq__.
163-
164-
__eq__ = object.__eq__
165-
__hash__ = object.__hash__
123+
...
124+
125+
def __enter__(self) -> Self: ...
126+
127+
def __exit__(self, *_: Any) -> None: ...

0 commit comments

Comments
 (0)