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

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

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

91.33
/src/ZTUtils/Lazy.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE
11
#
12
##############################################################################
13
_marker = object()
1✔
14

15

16
class Lazy:
1✔
17

18
    # Allow (reluctantly) access to unprotected attributes
19
    __allow_access_to_unprotected_subobjects__ = True
1✔
20
    _len = _marker
1✔
21
    _rlen = _marker
1✔
22

23
    @property
1✔
24
    def actual_result_count(self):
1✔
25
        if self._rlen is not _marker:
1✔
26
            return self._rlen
1✔
27
        self._rlen = len(self)
1✔
28
        return self._rlen
1✔
29

30
    @actual_result_count.setter
1✔
31
    def actual_result_count(self, value):
1✔
32
        self._rlen = value
1✔
33

34
    def __repr__(self):
1✔
35
        return repr(list(self))
1✔
36

37
    def __len__(self):
1✔
38
        # This is a worst-case len, subclasses should try to do better
39
        if self._len is not _marker:
1✔
40
            return self._len
1✔
41
        dl = len(self._data)
1✔
42
        while 1:
43
            try:
1✔
44
                self[dl]
1✔
45
                dl += 1
1✔
46
            except Exception:
1✔
47
                self._len = dl
1✔
48
                return dl
1✔
49

50
    def __add__(self, other):
1✔
51
        if not isinstance(other, Lazy):
1!
52
            raise TypeError(
×
53
                "Can not concatenate objects. Both must be lazy sequences.")
54
        return LazyCat([self, other])
1✔
55

56
    def __getitem__(self, index):
1✔
57
        if isinstance(index, slice):
1✔
58
            r = []
1✔
59
            start, stop, step = index.indices(len(self))
1✔
60
            for i in range(start, stop, step):
1✔
61
                try:
1✔
62
                    r.append(self[i])
1✔
63
                except IndexError:
×
64
                    return r
×
65
            return r
1✔
66

67
        # The single key lookup is implemented in the subclasses.
68
        raise NotImplementedError(
69
            'Lazy subclasses must implement __getitem__.')
70

71

72
class LazyCat(Lazy):
1✔
73
    """Lazy concatenation of one or more sequences. Should be handy
74
    for accessing small parts of big searches.
75
    """
76

77
    def __init__(self, sequences, length=None, actual_result_count=None):
1✔
78
        flattened_count = 0
1✔
79
        if len(sequences) < 100:
1✔
80
            # Optimize structure of LazyCats to avoid nesting
81
            # We don't do this for large numbers of input sequences
82
            # to make instantiation faster instead
83
            flattened_seq = []
1✔
84
            for s in sequences:
1✔
85
                if isinstance(s, LazyCat):
1✔
86
                    # If one of the sequences passed is itself a LazyCat, add
87
                    # its base sequences rather than nest LazyCats
88
                    if getattr(s, '_seq', None) is None:
1✔
89
                        flattened_seq.extend([s._data])
1✔
90
                    else:
91
                        flattened_seq.extend(s._seq)
1✔
92
                    flattened_count += s.actual_result_count
1✔
93
                elif isinstance(s, Lazy):
1✔
94
                    flattened_seq.append(s)
1✔
95
                    flattened_count += s.actual_result_count
1✔
96
                else:
97
                    flattened_seq.append(s)
1✔
98
                    flattened_count += len(s)
1✔
99
            sequences = flattened_seq
1✔
100
        self._seq = sequences
1✔
101
        self._data = []
1✔
102
        self._sindex = 0
1✔
103
        self._eindex = -1
1✔
104
        if length is not None:
1✔
105
            self._len = length
1✔
106
        if actual_result_count is not None:
1✔
107
            self.actual_result_count = actual_result_count
1✔
108
        else:
109
            self.actual_result_count = flattened_count
1✔
110

111
    def __getitem__(self, index):
1✔
112
        if isinstance(index, slice):
1✔
113
            return super().__getitem__(index)
1✔
114

115
        data = self._data
1✔
116
        try:
1✔
117
            seq = self._seq
1✔
118
        except AttributeError:
×
119
            return data[index]
×
120

121
        i = index
1✔
122
        if i < 0:
1!
123
            i = len(self) + i
×
124
        if i < 0:
1!
125
            raise IndexError(index)
×
126

127
        ind = len(data)
1✔
128
        if i < ind:
1!
129
            return data[i]
×
130
        ind = ind - 1
1✔
131

132
        sindex = self._sindex
1✔
133
        try:
1✔
134
            s = seq[sindex]
1✔
135
        except Exception:
1✔
136
            raise IndexError(index)
1✔
137
        eindex = self._eindex
1✔
138
        while i > ind:
1✔
139
            try:
1✔
140
                eindex = eindex + 1
1✔
141
                v = s[eindex]
1✔
142
                data.append(v)
1✔
143
                ind = ind + 1
1✔
144
            except IndexError:
1✔
145
                self._sindex = sindex = sindex + 1
1✔
146
                try:
1✔
147
                    s = self._seq[sindex]
1✔
148
                except Exception:
1✔
149
                    del self._seq
1✔
150
                    del self._sindex
1✔
151
                    del self._eindex
1✔
152
                    raise IndexError(index)
1✔
153
                self._eindex = eindex = -1
1✔
154
        self._eindex = eindex
1✔
155
        return data[i]
1✔
156

157
    def __len__(self):
1✔
158
        # Make len of LazyCat only as expensive as the lens
159
        # of its underlying sequences
160
        if self._len is not _marker:
1✔
161
            return self._len
1✔
162
        ld = 0
1✔
163
        try:
1✔
164
            for s in self._seq:
1✔
165
                ld += len(s)
1✔
166
        except AttributeError:
×
167
            ld = len(self._data)
×
168
        self._len = ld
1✔
169
        return ld
1✔
170

171

172
class LazyMap(Lazy):
1✔
173
    """Act like a sequence, but get data from a filtering process.
174
    Don't access data until necessary
175
    """
176

177
    def __init__(self, func, seq, length=None, actual_result_count=None):
1✔
178
        self._seq = seq
1✔
179
        self._data = {}
1✔
180
        self._func = func
1✔
181
        if length is not None:
1✔
182
            self._len = length
1✔
183
        else:
184
            self._len = len(seq)
1✔
185
        if actual_result_count is not None:
1✔
186
            self.actual_result_count = actual_result_count
1✔
187
        else:
188
            self.actual_result_count = self._len
1✔
189

190
    def __getitem__(self, index):
1✔
191
        if isinstance(index, slice):
1✔
192
            return super().__getitem__(index)
1✔
193

194
        data = self._data
1✔
195
        if index in data:
1✔
196
            return data[index]
1✔
197
        value = data[index] = self._func(self._seq[index])
1✔
198
        return value
1✔
199

200

201
class LazyFilter(Lazy):
1✔
202
    """Act like a sequence, but get data from a filtering process.
203
    Don't access data until necessary. Only data for which test(data)
204
    returns true will be considered part of the set.
205
    """
206

207
    def __init__(self, test, seq):
1✔
208
        self._seq = seq
1✔
209
        self._data = []
1✔
210
        self._eindex = -1
1✔
211
        self._test = test
1✔
212

213
    def __getitem__(self, index):
1✔
214
        if isinstance(index, slice):
1✔
215
            return super().__getitem__(index)
1✔
216

217
        data = self._data
1✔
218
        try:
1✔
219
            s = self._seq
1✔
220
        except AttributeError:
1✔
221
            return data[index]
1✔
222

223
        i = index
1✔
224
        if i < 0:
1!
225
            i = len(self) + i
×
226
        if i < 0:
1!
227
            raise IndexError(index)
×
228

229
        ind = len(data)
1✔
230
        if i < ind:
1!
231
            return data[i]
×
232
        ind = ind - 1
1✔
233

234
        test = self._test
1✔
235
        e = self._eindex
1✔
236
        while i > ind:
1✔
237
            try:
1✔
238
                e = e + 1
1✔
239
                v = s[e]
1✔
240
                if test(v):
1✔
241
                    data.append(v)
1✔
242
                    ind = ind + 1
1✔
243
            except IndexError:
1✔
244
                del self._test
1✔
245
                del self._seq
1✔
246
                del self._eindex
1✔
247
                raise IndexError(index)
1✔
248
        self._eindex = e
1✔
249
        return data[i]
1✔
250

251

252
class LazyMop(Lazy):
1✔
253
    """Act like a sequence, but get data from a filtering process.
254
    Don't access data until necessary. If the filter raises an exception
255
    for a given item, then that item isn't included in the sequence.
256
    """
257

258
    def __init__(self, test, seq):
1✔
259
        self._seq = seq
1✔
260
        self._data = []
1✔
261
        self._eindex = -1
1✔
262
        self._test = test
1✔
263

264
    def __getitem__(self, index):
1✔
265
        if isinstance(index, slice):
1✔
266
            return super().__getitem__(index)
1✔
267

268
        data = self._data
1✔
269
        try:
1✔
270
            s = self._seq
1✔
271
        except AttributeError:
1✔
272
            return data[index]
1✔
273

274
        i = index
1✔
275
        if i < 0:
1!
276
            i = len(self) + i
×
277
        if i < 0:
1!
278
            raise IndexError(index)
×
279

280
        ind = len(data)
1✔
281
        if i < ind:
1!
282
            return data[i]
×
283
        ind = ind - 1
1✔
284

285
        test = self._test
1✔
286
        e = self._eindex
1✔
287
        while i > ind:
1✔
288
            try:
1✔
289
                e = e + 1
1✔
290
                v = s[e]
1✔
291
                try:
1✔
292
                    v = test(v)
1✔
293
                    data.append(v)
1✔
294
                    ind = ind + 1
1✔
295
                except Exception:
1✔
296
                    pass
1✔
297
            except IndexError:
1✔
298
                del self._test
1✔
299
                del self._seq
1✔
300
                del self._eindex
1✔
301
                raise IndexError(index)
1✔
302
        self._eindex = e
1✔
303
        return data[i]
1✔
304

305

306
class LazyValues(Lazy):
1✔
307
    """Given a sequence of two tuples typically (key, value) act as
308
    though we are just a list of the values lazily"""
309

310
    def __init__(self, seq):
1✔
311
        self._seq = seq
1✔
312

313
    def __len__(self):
1✔
314
        if self._len is not _marker:
1✔
315
            return self._len
1✔
316
        self._len = len(self._seq)
1✔
317
        return self._len
1✔
318

319
    def __getitem__(self, index):
1✔
320
        if isinstance(index, slice):
1✔
321
            return self.__class__(self._seq[index])
1✔
322
        return self._seq[index][1]
1✔
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

© 2025 Coveralls, Inc