/
du.py
310 lines (247 loc) · 8.2 KB
/
du.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
from eventloop import EventLoop, my_print, printer
from functools import partial
import pdb
import queue
# https://promisesaplus.com/
class Promise():
"""
Promises are bound to one (singleton) instance of EventLoop (stored in variable `e`). Maybe it's not perfect, but it's ok
for the purpose of this assignment
"""
"""
states - did not work for some reason replaced with strings
"""
PENDING = 0;
FULFILLED = 1;
REJECTED = 2;
def __init__(self, resolver):
self.state = 'PENDING';
self.value = None;
self.values = [];
self.callbacks = [];
self.errorcallbacks = [];
self.specialCall = None;
self.count = 0;
if(resolver != None):
e.run(lambda :resolver(self.resolve, self.reject));
def setCount(self, val=0):
self.count = val;
def setList(self, list_of_promises):
self.list_of_promises = list_of_promises;
def setValueCall(value, call):# will call specialCall when fulfilled with value instead of self.value
if(self.state == 'PENDING'):
self.override = value;
self.specialCall = call;
def fulfill(self, value):
if(self.state != 'PENDING'):
return;
if(self.count != 0):
self.values.append(value);
if(self.count == len(self.values)):
self.value = self.values;
else:
return;
# print('fulfilled');
self.state = 'FULFILLED';
if(self.count == 0):
self.value = value;
else: ## this aproach is not cleanest but i cant be picky right now
values = [];
for i in range(len(self.list_of_promises)):
values.append(self.list_of_promises[i].value);
self.value = values;
callbacks = self.callbacks;
self.callbacks = None;
# print(len(callbacks));
for call in callbacks:
# print('callbacks salled');
e.run(lambda: call(self.value));
def reject(self, value):
if(self.state != 'PENDING'):
return;
if(not isinstance(value, Exception)):
return;
self.state = 'REJECTED';
self.value = value;
errorcalls = self.errorcallbacks;
self.errorcallbacks = None;
for call in errorcalls:
e.run(call(self.value));
"""
[RESOLVE](promise, x)
x is value -> fulfill with x
x is promise -> adopt promise state
-> pending -> wait and adopt
-> finished -> finish
x is promise-like - has then method resp. done
-> "promisify(x)" and adopt state
x is function -> not promise-like
-> fulfill promise with x
"""
def resolve(self, x):
if(self.state == 'FULFILLED'): # maybe some kind of error would be better
return;
if(isinstance(x, Promise)):
x.done(self.resolve, self.reject);
else:
self.fulfill(x);
def done(self, onFulfill, onReject):
#if(onFulfill is None):
# print('None');
if(self.state == 'PENDING'):
if(onFulfill is not None):
self.callbacks.append(onFulfill);
# print(self.callbacks);
if(onReject is not None):
self.errorcallbacks.append(onReject);
return;
if(self.state == 'FULFILLED'):
e.run(onFulfill(self.value));
if(self.state == REJECTED):
e.run(onReject(self.value));
def then(self, then_fn):
p = Promise(None);
def successCall(value):
if( hasattr(then_fn, '__call__') ):
e.run(lambda: p.resolve(then_fn(self.value)));
else:
e.run(lambda: p.resolve(self.value));
self.done(successCall, None); # errorcall is missing so none
return p;
def delayed(time, val = None):
"""
creates promise which fulfills after time `time` with value `val`; this is a complete
implementation, no TODO here.
"""
def resolver(resolve, reject):
e.wait(time, lambda: resolve(val))
return Promise(resolver)
def read_file(filename):
"""
creates promise which fulfills with the content of a given file
"""
#TODO
def resolver(resolve, reject):
try:
f = open(filename, 'r');
buff = f.read();
f.close();
resolve(buff);
except Exception as e:
reject(e);
return Promise(resolver);
def all(list_of_promises):
"""
Promise.all(list_of_promises) returns a new promise which fulfills when all the promises from
the given list fulfill. Result is a list cotaining values of individual promises in
list_of_promises. Similar to bluebird's Promise.all
"""
p = Promise(None);
p.setCount(len(list_of_promises));
p.setList(list_of_promises);
for pr in list_of_promises:
pr.then(p.resolve);
return p;
def foreach(iterable, get_promise):
"""
executes `get_promise` on each element from `iterable`. Results from `get_promise` are
waited for within the iteration.
foreach pseudocode:
for elem in iterable:
p = get_promise(elem)
wait for p to fulfill, then continue
"""
# can change to list of promises and agregate through all if return is wanted as list
x = iter(iterable);
val = next(x);
def resolver(resolve, reject):
e.run(lambda: resolve(get_promise(val)));
p = Promise(resolver);
def _foreach(iterator,p):
try:
elem = next(iterator);
#print(elem);
p = p.then(lambda v: get_promise(elem));
_foreach(iterator,p);
except StopIteration:
return
#promise_list = [];
_foreach(x,p);
# you may find helpful this piece of code
return p;
def print_inc_wait(res):
print(res)
res += 1
return Promise.delayed(1, res)
def test1():
"""
basic promise functionality.
should print begin, 0, 1, 2, 3 in one second intervals
moreover, `then` should return Promise object
"""
p = Promise.delayed(1,0) \
.then(print_inc_wait) \
.then(print_inc_wait) \
.then(print_inc_wait) \
.then(my_print)
print('begin')
assert(isinstance(p, Promise))
def test2():
"""
Test if promises are flattening returning values correctly.
Two snippets in this test should behave indistinguishibly from the outside world;
both should print '1' after 1 second delay
"""
Promise.delayed(1,0) \
.then(lambda x: x+1) \
.then(my_print)
Promise.delayed(1,0) \
.then(lambda x: Promise.delayed(1, x+1)) \
.then(my_print)
def test3():
"""
Two "Promise chains" may be run at once
should print 1 1 [delay] 2 2 [delay] 3 3
"""
def get_promise():
return Promise.delayed(1,0) \
.then(print_inc_wait) \
.then(print_inc_wait) \
.then(print_inc_wait)
get_promise()
get_promise()
def test4():
"""
creating new Promise works as it should
"""
def resolver(resolve, reject):
e.wait(2, lambda: resolve(10))
# ugly long line, what should I do about it?
Promise(resolver).then(lambda res: my_print("should print this after 2 seconds, moreover, this should be 10: %d"%res))
def test5():
"""
Tests Promise.all.
Should print [0, 2, 4, 8, 10] after 5 seconds (because the last Promise from the given list
fulfills after 5 seconds)
"""
list_of_promises = [Promise.delayed(6-i, 2*i) for i in range(6)];
Promise.all(list_of_promises).then(my_print);
def test6():
"""
Tests foreach.
Should print 1, [delay 1s], 2, [delay 2s], 3, [delay 3s], 4
"""
def f(x):
print('%d'%x)
return Promise.delayed(x, x)
Promise.foreach([1, 2, 3, 4], f)
e = EventLoop()
e.start()
#p = Promise(print_inc_wait());
#pdb.set_trace();
#test1()
#test2()
#test3()
#test4()
#test5()
test6()