Skip to content

Commit bfaec65

Browse files
authored
Make Transform an abstract base class, delete iapply method ... (#118)
...based on the `invertible` attribute. The iapply() itself is not abstract since it is strictly speaking not needed for a transform.
1 parent cdf4884 commit bfaec65

File tree

1 file changed

+39
-32
lines changed

1 file changed

+39
-32
lines changed

src/nnbench/io/transforms.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Metaclasses for defining transforms acting on benchmark records."""
22

3+
from abc import ABC, abstractmethod
34
from typing import Sequence
45

56
from nnbench.types import BenchmarkRecord
@@ -8,19 +9,18 @@
89
class Transform:
910
"""The basic transform which every transform has to inherit from."""
1011

11-
pass
12-
13-
14-
class OneToOneTransform(Transform):
1512
invertible: bool = True
1613
"""
1714
Whether this transform is invertible,
1815
i.e. records can be converted back and forth with no changes or data loss.
1916
"""
17+
pass
2018

19+
20+
class OneToOneTransform(ABC, Transform):
21+
@abstractmethod
2122
def apply(self, record: BenchmarkRecord) -> BenchmarkRecord:
22-
"""
23-
Apply this transform to a benchmark record.
23+
"""Apply this transform to a benchmark record.
2424
2525
Parameters
2626
----------
@@ -34,8 +34,7 @@ def apply(self, record: BenchmarkRecord) -> BenchmarkRecord:
3434
"""
3535

3636
def iapply(self, record: BenchmarkRecord) -> BenchmarkRecord:
37-
"""
38-
Apply the inverse of this transform.
37+
"""Apply the inverse of this transform.
3938
4039
In general, applying the inverse on a record not previously transformed
4140
may yield unexpected results.
@@ -49,25 +48,26 @@ def iapply(self, record: BenchmarkRecord) -> BenchmarkRecord:
4948
-------
5049
BenchmarkRecord
5150
The inversely transformed benchmark record.
51+
52+
Raises
53+
------
54+
RuntimeError
55+
If the `Transform.invertible` attribute is set to `False`.
5256
"""
57+
if not self.invertible:
58+
raise RuntimeError(f"{self.__class__.__name__}() is marked as not invertible")
59+
raise NotImplementedError
5360

5461

5562
class ManyToOneTransform(Transform):
56-
"""
57-
A many-to-one transform reducing a collection of records to a single record.
63+
"""A many-to-one transform reducing a collection of records to a single record.
5864
5965
This is useful for computing statistics on a collection of runs.
6066
"""
6167

62-
invertible: bool = True
63-
"""
64-
Whether this transform is invertible,
65-
i.e. records can be converted back and forth with no changes or data loss.
66-
"""
67-
68+
@abstractmethod
6869
def apply(self, record: Sequence[BenchmarkRecord]) -> BenchmarkRecord:
69-
"""
70-
Apply this transform to a benchmark record.
70+
"""Apply this transform to a benchmark record.
7171
7272
Parameters
7373
----------
@@ -82,8 +82,7 @@ def apply(self, record: Sequence[BenchmarkRecord]) -> BenchmarkRecord:
8282
"""
8383

8484
def iapply(self, record: BenchmarkRecord) -> Sequence[BenchmarkRecord]:
85-
"""
86-
Apply the inverse of this transform.
85+
"""Apply the inverse of this transform.
8786
8887
In general, applying the inverse on a record not previously transformed
8988
may yield unexpected results.
@@ -97,31 +96,32 @@ def iapply(self, record: BenchmarkRecord) -> Sequence[BenchmarkRecord]:
9796
-------
9897
Sequence[BenchmarkRecord]
9998
The inversely transformed benchmark record sequence.
99+
100+
Raises
101+
------
102+
RuntimeError
103+
If the `Transform.invertible` attribute is set to `False`.
100104
"""
101-
# TODO: Does this even make sense? Can't hurt to allow it on paper, though.
105+
if not self.invertible:
106+
raise RuntimeError(f"{self.__class__.__name__}() is marked as not invertible")
107+
raise NotImplementedError
102108

103109

104110
class ManyToManyTransform(Transform):
105-
"""
106-
A many-to-many transform mapping an input record collection to an output collection.
111+
"""A many-to-many transform mapping an input record collection to an output collection.
107112
108113
Use this to programmatically wrangle metadata or types in records, or to
109114
convert parameters into database-ready representations.
110115
"""
111116

112-
invertible: bool = True
113-
"""
114-
Whether this transform is invertible,
115-
i.e. records can be converted back and forth with no changes or data loss.
116-
"""
117117
length_invariant: bool = True
118118
"""
119119
Whether this transform preserves the number of records, i.e. no records are dropped.
120120
"""
121121

122+
@abstractmethod
122123
def apply(self, record: Sequence[BenchmarkRecord]) -> Sequence[BenchmarkRecord]:
123-
"""
124-
Apply this transform to a benchmark record.
124+
"""Apply this transform to a benchmark record.
125125
126126
Parameters
127127
----------
@@ -135,8 +135,7 @@ def apply(self, record: Sequence[BenchmarkRecord]) -> Sequence[BenchmarkRecord]:
135135
"""
136136

137137
def iapply(self, record: Sequence[BenchmarkRecord]) -> Sequence[BenchmarkRecord]:
138-
"""
139-
Apply the inverse of this transform.
138+
"""Apply the inverse of this transform.
140139
141140
In general, applying the inverse on a record not previously transformed
142141
may yield unexpected results.
@@ -150,4 +149,12 @@ def iapply(self, record: Sequence[BenchmarkRecord]) -> Sequence[BenchmarkRecord]
150149
-------
151150
Sequence[BenchmarkRecord]
152151
The inversely transformed benchmark record sequence.
152+
153+
Raises
154+
------
155+
RuntimeError
156+
If the `Transform.invertible` attribute is set to `False`.
153157
"""
158+
if not self.invertible:
159+
raise RuntimeError(f"{self.__class__.__name__}() is marked as not invertible")
160+
raise NotImplementedError

0 commit comments

Comments
 (0)