• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bethgelab / foolbox / 8137716344

22 Jan 2024 10:53PM UTC coverage: 98.47%. Remained the same
8137716344

push

github

web-flow
Bump pillow from 10.1.0 to 10.2.0 in /tests (#718)

Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.1.0...10.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

3475 of 3529 relevant lines covered (98.47%)

7.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/foolbox/models/base.py
1
from typing import TypeVar, Callable, Optional, Tuple, Any
10✔
2
from abc import ABC, abstractmethod
10✔
3
import copy
10✔
4
import eagerpy as ep
10✔
5

6
from ..types import Bounds, BoundsInput, Preprocessing
10✔
7
from ..devutils import atleast_kd
10✔
8

9

10
T = TypeVar("T")
10✔
11
PreprocessArgs = Tuple[Optional[ep.Tensor], Optional[ep.Tensor], Optional[int]]
10✔
12

13

14
class Model(ABC):
10✔
15
    @property
10✔
16
    @abstractmethod
17
    def bounds(self) -> Bounds:
18
        ...
19

20
    @abstractmethod  # noqa: F811
21
    def __call__(self, inputs: T) -> T:
22
        """Passes inputs through the model and returns the model's output"""
23
        ...
24

25
    def transform_bounds(self, bounds: BoundsInput) -> "Model":
10✔
26
        """Returns a new model with the desired bounds and updates the preprocessing accordingly"""
27
        # subclasses can provide more efficient implementations
28
        return TransformBoundsWrapper(self, bounds)
8✔
29

30

31
class TransformBoundsWrapper(Model):
10✔
32
    def __init__(self, model: Model, bounds: BoundsInput):
10✔
33
        self._model = model
10✔
34
        self._bounds = Bounds(*bounds)
10✔
35

36
    @property
10✔
37
    def bounds(self) -> Bounds:
10✔
38
        return self._bounds
8✔
39

40
    def __call__(self, inputs: T) -> T:
10✔
41
        x, restore_type = ep.astensor_(inputs)
8✔
42
        y = self._preprocess(x)
8✔
43
        z = self._model(y)
8✔
44
        return restore_type(z)
8✔
45

46
    def transform_bounds(self, bounds: BoundsInput, inplace: bool = False) -> Model:
10✔
47
        if inplace:
8✔
48
            self._bounds = Bounds(*bounds)
8✔
49
            return self
8✔
50
        else:
51
            # using the wrapped model instead of self to avoid
52
            # unnessary sequences of wrappers
53
            return TransformBoundsWrapper(self._model, bounds)
8✔
54

55
    def _preprocess(self, inputs: ep.TensorType) -> ep.TensorType:
10✔
56
        if self.bounds == self._model.bounds:
8✔
57
            return inputs
8✔
58

59
        # from bounds to (0, 1)
60
        min_, max_ = self.bounds
8✔
61
        x = (inputs - min_) / (max_ - min_)
8✔
62

63
        # from (0, 1) to wrapped model bounds
64
        min_, max_ = self._model.bounds
8✔
65
        return x * (max_ - min_) + min_
8✔
66

67
    @property
10✔
68
    def data_format(self) -> Any:
10✔
69
        return self._model.data_format  # type: ignore
10✔
70

71

72
ModelType = TypeVar("ModelType", bound="ModelWithPreprocessing")
10✔
73

74

75
class ModelWithPreprocessing(Model):
10✔
76
    def __init__(  # type: ignore
10✔
77
        self,
78
        model: Callable[..., ep.types.NativeTensor],
79
        bounds: BoundsInput,
80
        dummy: ep.Tensor,
81
        preprocessing: Preprocessing = None,
82
    ):
83
        if not callable(model):
6✔
84
            raise ValueError("expected model to be callable")  # pragma: no cover
85

86
        self._model = model
6✔
87
        self._bounds = Bounds(*bounds)
6✔
88
        self._dummy = dummy
6✔
89
        self._preprocess_args = self._process_preprocessing(preprocessing)
6✔
90

91
    @property
10✔
92
    def bounds(self) -> Bounds:
10✔
93
        return self._bounds
6✔
94

95
    @property
10✔
96
    def dummy(self) -> ep.Tensor:
10✔
97
        return self._dummy
6✔
98

99
    def __call__(self, inputs: T) -> T:
10✔
100
        x, restore_type = ep.astensor_(inputs)
6✔
101
        y = self._preprocess(x)
6✔
102
        z = ep.astensor(self._model(y.raw))
6✔
103
        return restore_type(z)
6✔
104

105
    def transform_bounds(
10✔
106
        self,
107
        bounds: BoundsInput,
108
        inplace: bool = False,
109
        wrapper: bool = False,
110
    ) -> Model:
111
        """Returns a new model with the desired bounds and updates the preprocessing accordingly"""
112
        # more efficient than the base class implementation because it avoids the additional wrapper
113

114
        if wrapper:
6✔
115
            if inplace:
6✔
116
                raise ValueError("inplace and wrapper cannot both be True")
6✔
117
            return super().transform_bounds(bounds)
6✔
118

119
        if self.bounds == bounds:
6✔
120
            if inplace:
6✔
121
                return self
6✔
122
            else:
123
                return copy.copy(self)
6✔
124

125
        a, b = self.bounds
6✔
126
        c, d = bounds
6✔
127
        f = (d - c) / (b - a)
6✔
128

129
        mean, std, flip_axis = self._preprocess_args
6✔
130

131
        if mean is None:
6✔
132
            mean = ep.zeros(self._dummy, 1)
6✔
133
        mean = f * (mean - a) + c
6✔
134

135
        if std is None:
6✔
136
            std = ep.ones(self._dummy, 1)
6✔
137
        std = f * std
6✔
138

139
        if inplace:
6✔
140
            model = self
6✔
141
        else:
142
            model = copy.copy(self)
6✔
143
        model._bounds = Bounds(*bounds)
6✔
144
        model._preprocess_args = (mean, std, flip_axis)
6✔
145
        return model
6✔
146

147
    def _preprocess(self, inputs: ep.Tensor) -> ep.Tensor:
10✔
148
        mean, std, flip_axis = self._preprocess_args
6✔
149
        x = inputs
6✔
150
        if flip_axis is not None:
6✔
151
            x = x.flip(axis=flip_axis)
2✔
152
        if mean is not None:
6✔
153
            x = x - mean
6✔
154
        if std is not None:
6✔
155
            x = x / std
6✔
156
        assert x.dtype == inputs.dtype
6✔
157
        return x
6✔
158

159
    def _process_preprocessing(self, preprocessing: Preprocessing) -> PreprocessArgs:
10✔
160
        if preprocessing is None:
6✔
161
            preprocessing = dict()
6✔
162

163
        unsupported = set(preprocessing.keys()) - {"mean", "std", "axis", "flip_axis"}
6✔
164
        if len(unsupported) > 0:
6✔
165
            raise ValueError(f"unknown preprocessing key: {unsupported.pop()}")
6✔
166

167
        mean = preprocessing.get("mean", None)
6✔
168
        std = preprocessing.get("std", None)
6✔
169
        axis = preprocessing.get("axis", None)
6✔
170
        flip_axis = preprocessing.get("flip_axis", None)
6✔
171

172
        def to_tensor(x: Any) -> Optional[ep.Tensor]:
6✔
173
            if x is None:
6✔
174
                return None
6✔
175
            if isinstance(x, ep.Tensor):
6✔
176
                return x
4✔
177
            try:
6✔
178
                y = ep.astensor(x)  # might raise ValueError
6✔
179
                if not isinstance(y, type(self._dummy)):
6✔
180
                    raise ValueError
6✔
181
                return y
4✔
182
            except ValueError:
6✔
183
                return ep.from_numpy(self._dummy, x)
6✔
184

185
        mean_ = to_tensor(mean)
6✔
186
        std_ = to_tensor(std)
6✔
187

188
        def apply_axis(x: Optional[ep.Tensor], axis: int) -> Optional[ep.Tensor]:
6✔
189
            if x is None:
6✔
190
                return None
6✔
191
            if x.ndim != 1:
6✔
192
                raise ValueError(f"non-None axis requires a 1D tensor, got {x.ndim}D")
6✔
193
            if axis >= 0:
6✔
194
                raise ValueError(
6✔
195
                    "expected axis to be None or negative, -1 refers to the last axis"
196
                )
197
            return atleast_kd(x, -axis)
6✔
198

199
        if axis is not None:
6✔
200
            mean_ = apply_axis(mean_, axis)
6✔
201
            std_ = apply_axis(std_, axis)
6✔
202

203
        return mean_, std_, flip_axis
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc