rx-rust 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rx_rust/__init__.py +1098 -0
- rx_rust-0.1.0.dist-info/METADATA +60 -0
- rx_rust-0.1.0.dist-info/RECORD +5 -0
- rx_rust-0.1.0.dist-info/WHEEL +5 -0
- rx_rust-0.1.0.dist-info/top_level.txt +1 -0
rx_rust/__init__.py
ADDED
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
"""Rx-Rust - Reactive Extensions for Python powered by Rust.
|
|
2
|
+
|
|
3
|
+
Rx-Rust 是一个用于组合异步和基于事件的程序的 Python 库,灵感来自微软的 Reactive Extensions (Rx) 库。
|
|
4
|
+
它建立在 Rust 之上,通过 PyO3 提供高性能的响应式编程体验。
|
|
5
|
+
|
|
6
|
+
核心概念:
|
|
7
|
+
Observable - 在未来可能发射 0 个或多个值的程序抽象
|
|
8
|
+
Observer - 订阅者,接收值的接收者
|
|
9
|
+
Operator - 转换和组合 Observable 的操作符
|
|
10
|
+
Subscription - 订阅句柄,用来取消订阅
|
|
11
|
+
Subject - 既是 Observable 又是 Observer,可以手动发射值
|
|
12
|
+
|
|
13
|
+
快速开始:
|
|
14
|
+
>>> import rx_rust
|
|
15
|
+
>>> result = []
|
|
16
|
+
>>> Rx-Rust.Observable.from_iter([1, 2, 3, 4, 5]) \
|
|
17
|
+
... .filter(lambda x: x % 2 == 0) \
|
|
18
|
+
... .map(lambda x: x * 10) \
|
|
19
|
+
... .subscribe(on_next=lambda v: result.append(v))
|
|
20
|
+
<Rx-Rust.Subscription object>
|
|
21
|
+
>>> result
|
|
22
|
+
[20, 40]
|
|
23
|
+
|
|
24
|
+
模块结构:
|
|
25
|
+
Observable - 可观察对象(核心类)
|
|
26
|
+
PublishSubject - 广播型主题(事件总线)
|
|
27
|
+
BehaviorSubject - 带当前值的主题
|
|
28
|
+
ReplaySubject - 重放历史值主题
|
|
29
|
+
CurrentThreadScheduler - 当前线程调度器(同步)
|
|
30
|
+
ThreadPoolScheduler - 线程池调度器(并发)
|
|
31
|
+
AsyncScheduler - 异步调度器
|
|
32
|
+
ImmediateScheduler - 立即调度器
|
|
33
|
+
Subscription - 订阅句柄
|
|
34
|
+
|
|
35
|
+
版本:0.1.0
|
|
36
|
+
许可:MIT
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import time
|
|
42
|
+
from typing import Any, Callable, Iterable, List, Optional
|
|
43
|
+
|
|
44
|
+
# ============================================================================
|
|
45
|
+
# 尝试加载 Rust 扩展
|
|
46
|
+
# ============================================================================
|
|
47
|
+
|
|
48
|
+
_USE_RUST = False
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
from . import rx_rust as _rust_mod # type: ignore
|
|
52
|
+
_USE_RUST = True
|
|
53
|
+
except (ImportError, AttributeError):
|
|
54
|
+
# 纯 Python 回退实现
|
|
55
|
+
_USE_RUST = False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ============================================================================
|
|
59
|
+
# Subscription - 订阅句柄
|
|
60
|
+
# ============================================================================
|
|
61
|
+
|
|
62
|
+
class Subscription:
|
|
63
|
+
"""表示对某个 Observable 的订阅句柄。
|
|
64
|
+
|
|
65
|
+
你可以通过调用 ``dispose()`` 来取消订阅。
|
|
66
|
+
一旦取消后,Observer 将不再接收任何值。
|
|
67
|
+
|
|
68
|
+
典型用法:
|
|
69
|
+
>>> sub = observable.subscribe(on_next=lambda x: print(x))
|
|
70
|
+
>>> # ... 之后
|
|
71
|
+
>>> sub.dispose() # 取消订阅
|
|
72
|
+
>>> sub.is_disposed()
|
|
73
|
+
True
|
|
74
|
+
|
|
75
|
+
注意事项:
|
|
76
|
+
Subscription 是不可重放的 —— 一旦取消后不能恢复;
|
|
77
|
+
多次调用 dispose() 是安全的(不会报错)。
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, inner=None):
|
|
81
|
+
"""创建一个新的 Subscription。
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
inner: 从底层实现返回的原始订阅对象。
|
|
85
|
+
"""
|
|
86
|
+
self._inner = inner
|
|
87
|
+
self._disposed = False
|
|
88
|
+
|
|
89
|
+
def dispose(self):
|
|
90
|
+
"""取消订阅。
|
|
91
|
+
|
|
92
|
+
调用后 Observer 将不再接收值。
|
|
93
|
+
这是一个幂等操作,多次调用安全。
|
|
94
|
+
|
|
95
|
+
返回:
|
|
96
|
+
None
|
|
97
|
+
|
|
98
|
+
示例:
|
|
99
|
+
>>> sub = Observable.from_iter([1, 2, 3]).subscribe(on_next=print)
|
|
100
|
+
>>> sub.dispose() # 取消订阅
|
|
101
|
+
"""
|
|
102
|
+
self._disposed = True
|
|
103
|
+
if self._inner is not None and hasattr(self._inner, "dispose"):
|
|
104
|
+
try:
|
|
105
|
+
self._inner.dispose()
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def is_disposed(self):
|
|
110
|
+
"""检查订阅是否已被取消。
|
|
111
|
+
|
|
112
|
+
返回:
|
|
113
|
+
bool: 如果订阅已取消则返回 True,否则返回 False。
|
|
114
|
+
|
|
115
|
+
示例:
|
|
116
|
+
>>> sub = Observable.of(42).subscribe(on_next=lambda x: None)
|
|
117
|
+
>>> sub.is_disposed()
|
|
118
|
+
False
|
|
119
|
+
>>> sub.dispose()
|
|
120
|
+
>>> sub.is_disposed()
|
|
121
|
+
True
|
|
122
|
+
"""
|
|
123
|
+
if self._inner is not None and hasattr(self._inner, "is_disposed"):
|
|
124
|
+
try:
|
|
125
|
+
return bool(self._inner.is_disposed())
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
return self._disposed
|
|
129
|
+
|
|
130
|
+
def __enter__(self):
|
|
131
|
+
"""进入 with 上下文。"""
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
135
|
+
"""退出 with 上下文时自动取消订阅。"""
|
|
136
|
+
self.dispose()
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def __repr__(self):
|
|
140
|
+
return f"Subscription(disposed={self.is_disposed()})"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ============================================================================
|
|
144
|
+
# Observable - 纯 Python 实现
|
|
145
|
+
# ============================================================================
|
|
146
|
+
|
|
147
|
+
class _PyObservable:
|
|
148
|
+
"""纯 Python 版 Observable。"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, subscribe_fn: Callable[[Callable[[Any], None]], Subscription]):
|
|
151
|
+
self._subscribe = subscribe_fn
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def of(value):
|
|
155
|
+
def _sub(observer):
|
|
156
|
+
observer(value)
|
|
157
|
+
return Subscription()
|
|
158
|
+
return _PyObservable(_sub)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def from_iter(values):
|
|
162
|
+
vals = list(values)
|
|
163
|
+
|
|
164
|
+
def _sub(observer):
|
|
165
|
+
for v in vals:
|
|
166
|
+
observer(v)
|
|
167
|
+
return Subscription()
|
|
168
|
+
return _PyObservable(_sub)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def range(start, count):
|
|
172
|
+
start = int(start)
|
|
173
|
+
count = int(count)
|
|
174
|
+
|
|
175
|
+
def _sub(observer):
|
|
176
|
+
for i in range(count):
|
|
177
|
+
observer(start + i)
|
|
178
|
+
return Subscription()
|
|
179
|
+
return _PyObservable(_sub)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def repeat(value, count):
|
|
183
|
+
count = int(count)
|
|
184
|
+
|
|
185
|
+
def _sub(observer):
|
|
186
|
+
for _ in range(count):
|
|
187
|
+
observer(value)
|
|
188
|
+
return Subscription()
|
|
189
|
+
return _PyObservable(_sub)
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def empty():
|
|
193
|
+
def _sub(observer):
|
|
194
|
+
return Subscription()
|
|
195
|
+
return _PyObservable(_sub)
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def never():
|
|
199
|
+
def _sub(observer):
|
|
200
|
+
return Subscription()
|
|
201
|
+
return _PyObservable(_sub)
|
|
202
|
+
|
|
203
|
+
def subscribe(self, on_next):
|
|
204
|
+
return self._subscribe(on_next)
|
|
205
|
+
|
|
206
|
+
# ---------- 转换操作符 ----------
|
|
207
|
+
def map(self, mapper):
|
|
208
|
+
source_subscribe = self._subscribe
|
|
209
|
+
|
|
210
|
+
def _sub(observer):
|
|
211
|
+
def wrapped(value):
|
|
212
|
+
try:
|
|
213
|
+
observer(mapper(value))
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
return source_subscribe(wrapped)
|
|
217
|
+
return _PyObservable(_sub)
|
|
218
|
+
|
|
219
|
+
def filter(self, predicate):
|
|
220
|
+
source_subscribe = self._subscribe
|
|
221
|
+
|
|
222
|
+
def _sub(observer):
|
|
223
|
+
def wrapped(value):
|
|
224
|
+
if predicate(value):
|
|
225
|
+
observer(value)
|
|
226
|
+
return source_subscribe(wrapped)
|
|
227
|
+
return _PyObservable(_sub)
|
|
228
|
+
|
|
229
|
+
# ---------- 过滤操作符 ----------
|
|
230
|
+
def take(self, n):
|
|
231
|
+
n = int(n)
|
|
232
|
+
source_subscribe = self._subscribe
|
|
233
|
+
|
|
234
|
+
def _sub(observer):
|
|
235
|
+
taken = [0]
|
|
236
|
+
|
|
237
|
+
def wrapped(value):
|
|
238
|
+
if taken[0] < n:
|
|
239
|
+
taken[0] += 1
|
|
240
|
+
observer(value)
|
|
241
|
+
return source_subscribe(wrapped)
|
|
242
|
+
return _PyObservable(_sub)
|
|
243
|
+
|
|
244
|
+
def skip(self, n):
|
|
245
|
+
n = int(n)
|
|
246
|
+
source_subscribe = self._subscribe
|
|
247
|
+
|
|
248
|
+
def _sub(observer):
|
|
249
|
+
skipped = [0]
|
|
250
|
+
|
|
251
|
+
def wrapped(value):
|
|
252
|
+
if skipped[0] < n:
|
|
253
|
+
skipped[0] += 1
|
|
254
|
+
return
|
|
255
|
+
observer(value)
|
|
256
|
+
return source_subscribe(wrapped)
|
|
257
|
+
return _PyObservable(_sub)
|
|
258
|
+
|
|
259
|
+
def first(self):
|
|
260
|
+
return self.take(1)
|
|
261
|
+
|
|
262
|
+
def last(self):
|
|
263
|
+
source_subscribe = self._subscribe
|
|
264
|
+
|
|
265
|
+
def _sub(observer):
|
|
266
|
+
last_value = [None]
|
|
267
|
+
have_value = [False]
|
|
268
|
+
|
|
269
|
+
def wrapped(value):
|
|
270
|
+
last_value[0] = value
|
|
271
|
+
have_value[0] = True
|
|
272
|
+
source_subscribe(wrapped)
|
|
273
|
+
if have_value[0]:
|
|
274
|
+
observer(last_value[0])
|
|
275
|
+
return Subscription()
|
|
276
|
+
return _PyObservable(_sub)
|
|
277
|
+
|
|
278
|
+
# ---------- 聚合操作符 ----------
|
|
279
|
+
def count(self):
|
|
280
|
+
source_subscribe = self._subscribe
|
|
281
|
+
|
|
282
|
+
def _sub(observer):
|
|
283
|
+
counter = [0]
|
|
284
|
+
|
|
285
|
+
def wrapped(value):
|
|
286
|
+
counter[0] += 1
|
|
287
|
+
source_subscribe(wrapped)
|
|
288
|
+
observer(counter[0])
|
|
289
|
+
return Subscription()
|
|
290
|
+
return _PyObservable(_sub)
|
|
291
|
+
|
|
292
|
+
def sum(self):
|
|
293
|
+
source_subscribe = self._subscribe
|
|
294
|
+
|
|
295
|
+
def _sub(observer):
|
|
296
|
+
total = [0]
|
|
297
|
+
has_value = [False]
|
|
298
|
+
|
|
299
|
+
def wrapped(value):
|
|
300
|
+
total[0] = total[0] + value
|
|
301
|
+
has_value[0] = True
|
|
302
|
+
source_subscribe(wrapped)
|
|
303
|
+
observer(total[0])
|
|
304
|
+
return Subscription()
|
|
305
|
+
|
|
306
|
+
return _PyObservable(_sub)
|
|
307
|
+
|
|
308
|
+
def reduce(self, initial, reducer):
|
|
309
|
+
source_subscribe = self._subscribe
|
|
310
|
+
|
|
311
|
+
def _sub(observer):
|
|
312
|
+
acc = [initial]
|
|
313
|
+
|
|
314
|
+
def wrapped(value):
|
|
315
|
+
acc[0] = reducer(acc[0], value)
|
|
316
|
+
source_subscribe(wrapped)
|
|
317
|
+
observer(acc[0])
|
|
318
|
+
return Subscription()
|
|
319
|
+
return _PyObservable(_sub)
|
|
320
|
+
|
|
321
|
+
def scan(self, initial, scanner):
|
|
322
|
+
source_subscribe = self._subscribe
|
|
323
|
+
|
|
324
|
+
def _sub(observer):
|
|
325
|
+
acc = [initial]
|
|
326
|
+
observer(acc[0])
|
|
327
|
+
|
|
328
|
+
def wrapped(value):
|
|
329
|
+
acc[0] = scanner(acc[0], value)
|
|
330
|
+
observer(acc[0])
|
|
331
|
+
source_subscribe(wrapped)
|
|
332
|
+
return Subscription()
|
|
333
|
+
return _PyObservable(_sub)
|
|
334
|
+
|
|
335
|
+
def flat_map(self, mapper):
|
|
336
|
+
source_subscribe = self._subscribe
|
|
337
|
+
|
|
338
|
+
def _sub(observer):
|
|
339
|
+
def wrapped(value):
|
|
340
|
+
inner = mapper(value)
|
|
341
|
+
# 兼容:薄包装 Observable 或 _PyObservable
|
|
342
|
+
if hasattr(inner, "subscribe"):
|
|
343
|
+
inner.subscribe(on_next=lambda v: observer(v))
|
|
344
|
+
elif hasattr(inner, "_subscribe"):
|
|
345
|
+
inner._subscribe(observer)
|
|
346
|
+
else:
|
|
347
|
+
try:
|
|
348
|
+
for item in inner:
|
|
349
|
+
observer(item)
|
|
350
|
+
except TypeError:
|
|
351
|
+
observer(inner)
|
|
352
|
+
return source_subscribe(wrapped)
|
|
353
|
+
return _PyObservable(_sub)
|
|
354
|
+
|
|
355
|
+
def start_with(self, *values):
|
|
356
|
+
source_subscribe = self._subscribe
|
|
357
|
+
|
|
358
|
+
def _sub(observer):
|
|
359
|
+
for v in values:
|
|
360
|
+
observer(v)
|
|
361
|
+
return source_subscribe(observer)
|
|
362
|
+
return _PyObservable(_sub)
|
|
363
|
+
|
|
364
|
+
def default_if_empty(self, default):
|
|
365
|
+
source_subscribe = self._subscribe
|
|
366
|
+
|
|
367
|
+
def _sub(observer):
|
|
368
|
+
emitted = [False]
|
|
369
|
+
|
|
370
|
+
def wrapped(value):
|
|
371
|
+
emitted[0] = True
|
|
372
|
+
observer(value)
|
|
373
|
+
source_subscribe(wrapped)
|
|
374
|
+
if not emitted[0]:
|
|
375
|
+
observer(default)
|
|
376
|
+
return Subscription()
|
|
377
|
+
return _PyObservable(_sub)
|
|
378
|
+
|
|
379
|
+
def contains(self, target):
|
|
380
|
+
source_subscribe = self._subscribe
|
|
381
|
+
|
|
382
|
+
def _sub(observer):
|
|
383
|
+
found = [False]
|
|
384
|
+
|
|
385
|
+
def wrapped(value):
|
|
386
|
+
if not found[0]:
|
|
387
|
+
if value == target:
|
|
388
|
+
found[0] = True
|
|
389
|
+
source_subscribe(wrapped)
|
|
390
|
+
observer(found[0])
|
|
391
|
+
return Subscription()
|
|
392
|
+
return _PyObservable(_sub)
|
|
393
|
+
|
|
394
|
+
def all(self, predicate):
|
|
395
|
+
source_subscribe = self._subscribe
|
|
396
|
+
|
|
397
|
+
def _sub(observer):
|
|
398
|
+
all_pass = [True]
|
|
399
|
+
|
|
400
|
+
def wrapped(value):
|
|
401
|
+
if all_pass[0]:
|
|
402
|
+
if not predicate(value):
|
|
403
|
+
all_pass[0] = False
|
|
404
|
+
source_subscribe(wrapped)
|
|
405
|
+
observer(all_pass[0])
|
|
406
|
+
return Subscription()
|
|
407
|
+
return _PyObservable(_sub)
|
|
408
|
+
|
|
409
|
+
def do_on_next(self, action):
|
|
410
|
+
source_subscribe = self._subscribe
|
|
411
|
+
|
|
412
|
+
def _sub(observer):
|
|
413
|
+
def wrapped(value):
|
|
414
|
+
action(value)
|
|
415
|
+
observer(value)
|
|
416
|
+
return source_subscribe(wrapped)
|
|
417
|
+
return _PyObservable(_sub)
|
|
418
|
+
|
|
419
|
+
def merge(self, other):
|
|
420
|
+
source_subscribe = self._subscribe
|
|
421
|
+
other_inner = getattr(other, "_inner", None)
|
|
422
|
+
if other_inner is None and hasattr(other, "_subscribe"):
|
|
423
|
+
other_subscribe = other._subscribe
|
|
424
|
+
elif other_inner is not None and hasattr(other_inner, "subscribe"):
|
|
425
|
+
def other_subscribe(observer):
|
|
426
|
+
other_inner.subscribe(observer)
|
|
427
|
+
else:
|
|
428
|
+
def other_subscribe(observer):
|
|
429
|
+
return Subscription()
|
|
430
|
+
|
|
431
|
+
def _sub(observer):
|
|
432
|
+
source_subscribe(observer)
|
|
433
|
+
other_subscribe(observer)
|
|
434
|
+
return Subscription()
|
|
435
|
+
return _PyObservable(_sub)
|
|
436
|
+
|
|
437
|
+
def concat(self, other):
|
|
438
|
+
return self.merge(other)
|
|
439
|
+
|
|
440
|
+
def collect(self):
|
|
441
|
+
"""收集所有发射的值到一个列表(阻塞操作)。"""
|
|
442
|
+
items: List[Any] = []
|
|
443
|
+
|
|
444
|
+
def observer(value):
|
|
445
|
+
items.append(value)
|
|
446
|
+
self._subscribe(observer)
|
|
447
|
+
return list(items)
|
|
448
|
+
|
|
449
|
+
def run(self):
|
|
450
|
+
self._subscribe(lambda _: None)
|
|
451
|
+
return self
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
# ============================================================================
|
|
455
|
+
# Subject - 纯 Python 实现
|
|
456
|
+
# ============================================================================
|
|
457
|
+
|
|
458
|
+
class _PyPublishSubject:
|
|
459
|
+
def __init__(self):
|
|
460
|
+
self._observers = []
|
|
461
|
+
|
|
462
|
+
def on_next(self, value):
|
|
463
|
+
for obs in list(self._observers):
|
|
464
|
+
if not obs[1].is_disposed():
|
|
465
|
+
obs[0](value)
|
|
466
|
+
|
|
467
|
+
def on_completed(self):
|
|
468
|
+
self._observers.clear()
|
|
469
|
+
|
|
470
|
+
def subscribe(self, on_next):
|
|
471
|
+
sub = Subscription()
|
|
472
|
+
self._observers.append((on_next, sub))
|
|
473
|
+
return sub
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
class _PyBehaviorSubject:
|
|
477
|
+
def __init__(self, initial_value):
|
|
478
|
+
self._current = initial_value
|
|
479
|
+
self._observers = []
|
|
480
|
+
|
|
481
|
+
def on_next(self, value):
|
|
482
|
+
self._current = value
|
|
483
|
+
for obs in list(self._observers):
|
|
484
|
+
if not obs[1].is_disposed():
|
|
485
|
+
obs[0](value)
|
|
486
|
+
|
|
487
|
+
def on_completed(self):
|
|
488
|
+
self._observers.clear()
|
|
489
|
+
|
|
490
|
+
def subscribe(self, on_next):
|
|
491
|
+
sub = Subscription()
|
|
492
|
+
on_next(self._current)
|
|
493
|
+
self._observers.append((on_next, sub))
|
|
494
|
+
return sub
|
|
495
|
+
|
|
496
|
+
@property
|
|
497
|
+
def value(self):
|
|
498
|
+
return self._current
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
class _PyReplaySubject:
|
|
502
|
+
def __init__(self, capacity):
|
|
503
|
+
self._capacity = int(capacity)
|
|
504
|
+
self._buffer = []
|
|
505
|
+
self._observers = []
|
|
506
|
+
|
|
507
|
+
def on_next(self, value):
|
|
508
|
+
self._buffer.append(value)
|
|
509
|
+
if len(self._buffer) > self._capacity:
|
|
510
|
+
self._buffer.pop(0)
|
|
511
|
+
for obs in list(self._observers):
|
|
512
|
+
if not obs[1].is_disposed():
|
|
513
|
+
obs[0](value)
|
|
514
|
+
|
|
515
|
+
def on_completed(self):
|
|
516
|
+
self._observers.clear()
|
|
517
|
+
|
|
518
|
+
def subscribe(self, on_next):
|
|
519
|
+
sub = Subscription()
|
|
520
|
+
for buffered in self._buffer:
|
|
521
|
+
on_next(buffered)
|
|
522
|
+
self._observers.append((on_next, sub))
|
|
523
|
+
return sub
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# ============================================================================
|
|
527
|
+
# Scheduler - 纯 Python 实现
|
|
528
|
+
# ============================================================================
|
|
529
|
+
|
|
530
|
+
class _PyCurrentThreadScheduler:
|
|
531
|
+
def now(self):
|
|
532
|
+
return time.time() * 1000.0
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class _PyThreadPoolScheduler:
|
|
536
|
+
def __init__(self, num_threads):
|
|
537
|
+
self._num_threads = int(num_threads)
|
|
538
|
+
|
|
539
|
+
def now(self):
|
|
540
|
+
return time.time() * 1000.0
|
|
541
|
+
|
|
542
|
+
def get_num_threads(self):
|
|
543
|
+
return self._num_threads
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class _PyAsyncScheduler:
|
|
547
|
+
def now(self):
|
|
548
|
+
return time.time() * 1000.0
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
class _PyImmediateScheduler:
|
|
552
|
+
def now(self):
|
|
553
|
+
return time.time() * 1000.0
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
# ============================================================================
|
|
557
|
+
# 公共 API: Observable
|
|
558
|
+
# ============================================================================
|
|
559
|
+
|
|
560
|
+
class Observable:
|
|
561
|
+
"""表示一个在未来可能发射 0 个或多个值的可观察对象。
|
|
562
|
+
|
|
563
|
+
这是 Rx-Rust 的核心类。你可以:
|
|
564
|
+
|
|
565
|
+
1. 通过静态工厂方法创建 Observable:
|
|
566
|
+
- ``of(value)`` - 发射单个值
|
|
567
|
+
- ``from_iter(iterable)`` - 发射迭代器中所有值
|
|
568
|
+
- ``range(start, count)`` - 发射 count 个连续整数
|
|
569
|
+
- ``repeat(value, n)`` - 重复发射 value n 次
|
|
570
|
+
- ``empty()`` - 什么都不发射,立即完成
|
|
571
|
+
- ``never()`` - 什么都不发射,也不完成
|
|
572
|
+
|
|
573
|
+
2. 通过操作符组合和转换:
|
|
574
|
+
- ``map(mapper)`` - 一对一转换
|
|
575
|
+
- ``filter(predicate)`` - 条件过滤
|
|
576
|
+
- ``take(n)`` - 只取前 n 个
|
|
577
|
+
- ``skip(n)`` - 跳过前 n 个
|
|
578
|
+
- ``first()`` - 只取第一个
|
|
579
|
+
- ``reduce(initial, fn)`` - 累积并发射结果
|
|
580
|
+
|
|
581
|
+
3. 通过 subscribe() 订阅:
|
|
582
|
+
- ``subscribe(on_next=..., on_completed=...)``
|
|
583
|
+
|
|
584
|
+
示例:
|
|
585
|
+
>>> result = []
|
|
586
|
+
>>> Observable.from_iter([1, 2, 3, 4, 5]) \
|
|
587
|
+
... .filter(lambda x: x % 2 == 0) \
|
|
588
|
+
... .map(lambda x: x * 10) \
|
|
589
|
+
... .subscribe(on_next=lambda v: result.append(v))
|
|
590
|
+
>>> result
|
|
591
|
+
[20, 40]
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
def __init__(self, inner):
|
|
595
|
+
"""创建一个新的 Observable(通常从底层实现对象包装)。"""
|
|
596
|
+
self._inner = inner
|
|
597
|
+
|
|
598
|
+
# ---------- 静态工厂方法 ----------
|
|
599
|
+
@staticmethod
|
|
600
|
+
def of(value):
|
|
601
|
+
"""创建一个发射单个值然后完成的 Observable。"""
|
|
602
|
+
if _USE_RUST:
|
|
603
|
+
try:
|
|
604
|
+
return Observable(_rust_mod.Observable.of(value))
|
|
605
|
+
except Exception:
|
|
606
|
+
pass
|
|
607
|
+
return Observable(_PyObservable.of(value))
|
|
608
|
+
|
|
609
|
+
@staticmethod
|
|
610
|
+
def from_iter(values):
|
|
611
|
+
"""从一个可迭代对象创建 Observable,按顺序发射每个值。"""
|
|
612
|
+
if _USE_RUST:
|
|
613
|
+
try:
|
|
614
|
+
return Observable(_rust_mod.Observable.from_iter(list(values)))
|
|
615
|
+
except Exception:
|
|
616
|
+
pass
|
|
617
|
+
return Observable(_PyObservable.from_iter(values))
|
|
618
|
+
|
|
619
|
+
@staticmethod
|
|
620
|
+
def range(start, count):
|
|
621
|
+
"""创建一个发射连续整数范围的 Observable。"""
|
|
622
|
+
if _USE_RUST:
|
|
623
|
+
try:
|
|
624
|
+
return Observable(_rust_mod.Observable.range(int(start), int(count)))
|
|
625
|
+
except Exception:
|
|
626
|
+
pass
|
|
627
|
+
return Observable(_PyObservable.range(start, count))
|
|
628
|
+
|
|
629
|
+
@staticmethod
|
|
630
|
+
def repeat(value, count):
|
|
631
|
+
"""创建一个重复发射同一个值 count 次的 Observable。"""
|
|
632
|
+
if _USE_RUST:
|
|
633
|
+
try:
|
|
634
|
+
return Observable(_rust_mod.Observable.repeat(value, int(count)))
|
|
635
|
+
except Exception:
|
|
636
|
+
pass
|
|
637
|
+
return Observable(_PyObservable.repeat(value, count))
|
|
638
|
+
|
|
639
|
+
@staticmethod
|
|
640
|
+
def empty():
|
|
641
|
+
"""创建一个什么都不发射、立即完成的 Observable。"""
|
|
642
|
+
if _USE_RUST:
|
|
643
|
+
try:
|
|
644
|
+
return Observable(_rust_mod.Observable.empty())
|
|
645
|
+
except Exception:
|
|
646
|
+
pass
|
|
647
|
+
return Observable(_PyObservable.empty())
|
|
648
|
+
|
|
649
|
+
@staticmethod
|
|
650
|
+
def never():
|
|
651
|
+
"""创建一个什么都不发射、也不完成的 Observable。"""
|
|
652
|
+
if _USE_RUST:
|
|
653
|
+
try:
|
|
654
|
+
return Observable(_rust_mod.Observable.never())
|
|
655
|
+
except Exception:
|
|
656
|
+
pass
|
|
657
|
+
return Observable(_PyObservable.never())
|
|
658
|
+
|
|
659
|
+
# ---------- 订阅方法 ----------
|
|
660
|
+
def subscribe(self, on_next=None, on_error=None, on_completed=None):
|
|
661
|
+
"""订阅 Observable,开始接收值。"""
|
|
662
|
+
next_cb = on_next if on_next is not None else (lambda v: None)
|
|
663
|
+
if _USE_RUST and hasattr(self._inner, "subscribe"):
|
|
664
|
+
try:
|
|
665
|
+
rust_sub = self._inner.subscribe(next_cb)
|
|
666
|
+
if isinstance(rust_sub, Subscription):
|
|
667
|
+
return rust_sub
|
|
668
|
+
return Subscription(rust_sub)
|
|
669
|
+
except Exception:
|
|
670
|
+
pass
|
|
671
|
+
return self._inner.subscribe(next_cb)
|
|
672
|
+
|
|
673
|
+
# ---------- 转换操作符 ----------
|
|
674
|
+
def map(self, mapper):
|
|
675
|
+
"""对每个发射的值应用映射函数。"""
|
|
676
|
+
if _USE_RUST and hasattr(self._inner, "map"):
|
|
677
|
+
try:
|
|
678
|
+
return Observable(self._inner.map(mapper))
|
|
679
|
+
except Exception:
|
|
680
|
+
pass
|
|
681
|
+
return Observable(self._inner.map(mapper))
|
|
682
|
+
|
|
683
|
+
def filter(self, predicate):
|
|
684
|
+
"""只发射满足条件的值。"""
|
|
685
|
+
if _USE_RUST and hasattr(self._inner, "filter"):
|
|
686
|
+
try:
|
|
687
|
+
return Observable(self._inner.filter(predicate))
|
|
688
|
+
except Exception:
|
|
689
|
+
pass
|
|
690
|
+
return Observable(self._inner.filter(predicate))
|
|
691
|
+
|
|
692
|
+
# ---------- 过滤操作符 ----------
|
|
693
|
+
def take(self, n):
|
|
694
|
+
"""只发射前 n 个值。"""
|
|
695
|
+
if _USE_RUST and hasattr(self._inner, "take"):
|
|
696
|
+
try:
|
|
697
|
+
return Observable(self._inner.take(int(n)))
|
|
698
|
+
except Exception:
|
|
699
|
+
pass
|
|
700
|
+
return Observable(self._inner.take(n))
|
|
701
|
+
|
|
702
|
+
def skip(self, n):
|
|
703
|
+
"""跳过前 n 个值。"""
|
|
704
|
+
if _USE_RUST and hasattr(self._inner, "skip"):
|
|
705
|
+
try:
|
|
706
|
+
return Observable(self._inner.skip(int(n)))
|
|
707
|
+
except Exception:
|
|
708
|
+
pass
|
|
709
|
+
return Observable(self._inner.skip(n))
|
|
710
|
+
|
|
711
|
+
def first(self):
|
|
712
|
+
"""只发射第一个值。"""
|
|
713
|
+
if _USE_RUST and hasattr(self._inner, "first"):
|
|
714
|
+
try:
|
|
715
|
+
return Observable(self._inner.first())
|
|
716
|
+
except Exception:
|
|
717
|
+
pass
|
|
718
|
+
return Observable(self._inner.first())
|
|
719
|
+
|
|
720
|
+
def last(self):
|
|
721
|
+
"""只发射最后一个值。"""
|
|
722
|
+
if _USE_RUST and hasattr(self._inner, "last"):
|
|
723
|
+
try:
|
|
724
|
+
return Observable(self._inner.last())
|
|
725
|
+
except Exception:
|
|
726
|
+
pass
|
|
727
|
+
return Observable(self._inner.last())
|
|
728
|
+
|
|
729
|
+
def count(self):
|
|
730
|
+
"""发射收到的值总数量。"""
|
|
731
|
+
if _USE_RUST and hasattr(self._inner, "count"):
|
|
732
|
+
try:
|
|
733
|
+
return Observable(self._inner.count())
|
|
734
|
+
except Exception:
|
|
735
|
+
pass
|
|
736
|
+
return Observable(self._inner.count())
|
|
737
|
+
|
|
738
|
+
def sum(self):
|
|
739
|
+
"""发射所有值的累加和。"""
|
|
740
|
+
if _USE_RUST and hasattr(self._inner, "sum"):
|
|
741
|
+
try:
|
|
742
|
+
return Observable(self._inner.sum())
|
|
743
|
+
except Exception:
|
|
744
|
+
pass
|
|
745
|
+
return Observable(self._inner.sum())
|
|
746
|
+
|
|
747
|
+
# ---------- 聚合/累积操作符 ----------
|
|
748
|
+
def reduce(self, initial, reducer):
|
|
749
|
+
"""累积所有值,在完成时发射最终累积结果。"""
|
|
750
|
+
if _USE_RUST and hasattr(self._inner, "reduce"):
|
|
751
|
+
try:
|
|
752
|
+
return Observable(self._inner.reduce(initial, reducer))
|
|
753
|
+
except Exception:
|
|
754
|
+
pass
|
|
755
|
+
return Observable(self._inner.reduce(initial, reducer))
|
|
756
|
+
|
|
757
|
+
def scan(self, initial, scanner):
|
|
758
|
+
"""逐步累积并发射每一步的中间结果。"""
|
|
759
|
+
if _USE_RUST and hasattr(self._inner, "scan"):
|
|
760
|
+
try:
|
|
761
|
+
return Observable(self._inner.scan(initial, scanner))
|
|
762
|
+
except Exception:
|
|
763
|
+
pass
|
|
764
|
+
return Observable(self._inner.scan(initial, scanner))
|
|
765
|
+
|
|
766
|
+
def flat_map(self, mapper):
|
|
767
|
+
"""对每个值应用 mapper,然后将结果展平发射。"""
|
|
768
|
+
if _USE_RUST and hasattr(self._inner, "flat_map"):
|
|
769
|
+
try:
|
|
770
|
+
return Observable(self._inner.flat_map(mapper))
|
|
771
|
+
except Exception:
|
|
772
|
+
pass
|
|
773
|
+
return Observable(self._inner.flat_map(mapper))
|
|
774
|
+
|
|
775
|
+
def start_with(self, *values):
|
|
776
|
+
"""在序列开头插入一个或多个值。"""
|
|
777
|
+
if _USE_RUST and hasattr(self._inner, "start_with"):
|
|
778
|
+
try:
|
|
779
|
+
return Observable(self._inner.start_with(*values))
|
|
780
|
+
except Exception:
|
|
781
|
+
pass
|
|
782
|
+
return Observable(self._inner.start_with(*values))
|
|
783
|
+
|
|
784
|
+
def default_if_empty(self, default):
|
|
785
|
+
"""如果源为空则发射 default,否则与源相同。"""
|
|
786
|
+
if _USE_RUST and hasattr(self._inner, "default_if_empty"):
|
|
787
|
+
try:
|
|
788
|
+
return Observable(self._inner.default_if_empty(default))
|
|
789
|
+
except Exception:
|
|
790
|
+
pass
|
|
791
|
+
return Observable(self._inner.default_if_empty(default))
|
|
792
|
+
|
|
793
|
+
def contains(self, target):
|
|
794
|
+
"""检测序列中是否包含 target,发射单个布尔值。"""
|
|
795
|
+
if _USE_RUST and hasattr(self._inner, "contains"):
|
|
796
|
+
try:
|
|
797
|
+
return Observable(self._inner.contains(target))
|
|
798
|
+
except Exception:
|
|
799
|
+
pass
|
|
800
|
+
return Observable(self._inner.contains(target))
|
|
801
|
+
|
|
802
|
+
def all(self, predicate):
|
|
803
|
+
"""检测是否所有值都满足 predicate,发射单个布尔值。"""
|
|
804
|
+
if _USE_RUST and hasattr(self._inner, "all"):
|
|
805
|
+
try:
|
|
806
|
+
return Observable(self._inner.all(predicate))
|
|
807
|
+
except Exception:
|
|
808
|
+
pass
|
|
809
|
+
return Observable(self._inner.all(predicate))
|
|
810
|
+
|
|
811
|
+
def do_on_next(self, action):
|
|
812
|
+
"""为每个 on_next 产生副作用但不改变值本身。"""
|
|
813
|
+
if _USE_RUST and hasattr(self._inner, "do_on_next"):
|
|
814
|
+
try:
|
|
815
|
+
return Observable(self._inner.do_on_next(action))
|
|
816
|
+
except Exception:
|
|
817
|
+
pass
|
|
818
|
+
return Observable(self._inner.do_on_next(action))
|
|
819
|
+
|
|
820
|
+
def merge(self, other):
|
|
821
|
+
"""将两个 Observable 的值合并到一个序列中。"""
|
|
822
|
+
if _USE_RUST and hasattr(self._inner, "merge"):
|
|
823
|
+
try:
|
|
824
|
+
other_inner = getattr(other, "_inner", other)
|
|
825
|
+
return Observable(self._inner.merge(other_inner))
|
|
826
|
+
except Exception:
|
|
827
|
+
pass
|
|
828
|
+
return Observable(self._inner.merge(other))
|
|
829
|
+
|
|
830
|
+
def concat(self, other):
|
|
831
|
+
"""连接两个 Observable(同步模式下等同于 merge)。"""
|
|
832
|
+
if _USE_RUST and hasattr(self._inner, "concat"):
|
|
833
|
+
try:
|
|
834
|
+
other_inner = getattr(other, "_inner", other)
|
|
835
|
+
return Observable(self._inner.concat(other_inner))
|
|
836
|
+
except Exception:
|
|
837
|
+
pass
|
|
838
|
+
return Observable(self._inner.concat(other))
|
|
839
|
+
|
|
840
|
+
# ---------- 收集 ----------
|
|
841
|
+
def collect(self):
|
|
842
|
+
"""收集所有发射的值到一个列表(阻塞操作)。"""
|
|
843
|
+
if _USE_RUST and hasattr(self._inner, "collect"):
|
|
844
|
+
try:
|
|
845
|
+
return list(self._inner.collect())
|
|
846
|
+
except Exception:
|
|
847
|
+
pass
|
|
848
|
+
return self._inner.collect()
|
|
849
|
+
|
|
850
|
+
def run(self):
|
|
851
|
+
"""订阅并消费所有值。"""
|
|
852
|
+
if _USE_RUST and hasattr(self._inner, "run"):
|
|
853
|
+
try:
|
|
854
|
+
self._inner.run()
|
|
855
|
+
except Exception:
|
|
856
|
+
self._inner.subscribe(lambda _: None)
|
|
857
|
+
else:
|
|
858
|
+
self._inner.subscribe(lambda _: None)
|
|
859
|
+
return self
|
|
860
|
+
|
|
861
|
+
def __repr__(self):
|
|
862
|
+
return "Observable()"
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
# ============================================================================
|
|
866
|
+
# Subject - 广播型主题
|
|
867
|
+
# ============================================================================
|
|
868
|
+
|
|
869
|
+
class PublishSubject:
|
|
870
|
+
"""广播型主题。向所有订阅者广播发射的值。
|
|
871
|
+
|
|
872
|
+
新订阅者只能收到订阅之后发射的值。
|
|
873
|
+
适合用作事件总线、信号分发等场景。
|
|
874
|
+
"""
|
|
875
|
+
|
|
876
|
+
def __init__(self):
|
|
877
|
+
"""创建一个新的 PublishSubject。"""
|
|
878
|
+
if _USE_RUST:
|
|
879
|
+
try:
|
|
880
|
+
self._inner = _rust_mod.PublishSubject()
|
|
881
|
+
except Exception:
|
|
882
|
+
self._inner = _PyPublishSubject()
|
|
883
|
+
else:
|
|
884
|
+
self._inner = _PyPublishSubject()
|
|
885
|
+
|
|
886
|
+
def on_next(self, value):
|
|
887
|
+
"""向所有订阅者发射一个值。"""
|
|
888
|
+
if hasattr(self._inner, "on_next"):
|
|
889
|
+
self._inner.on_next(value)
|
|
890
|
+
|
|
891
|
+
def on_completed(self):
|
|
892
|
+
"""向所有订阅者发出完成信号。"""
|
|
893
|
+
if hasattr(self._inner, "on_completed"):
|
|
894
|
+
self._inner.on_completed()
|
|
895
|
+
|
|
896
|
+
def subscribe(self, on_next=None, on_error=None, on_completed=None):
|
|
897
|
+
"""订阅这个 Subject,接收后续发射的值。"""
|
|
898
|
+
next_cb = on_next if on_next is not None else (lambda v: None)
|
|
899
|
+
rust_sub = self._inner.subscribe(next_cb)
|
|
900
|
+
if isinstance(rust_sub, Subscription):
|
|
901
|
+
return rust_sub
|
|
902
|
+
return Subscription(rust_sub)
|
|
903
|
+
|
|
904
|
+
def __repr__(self):
|
|
905
|
+
return "PublishSubject()"
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
class BehaviorSubject:
|
|
909
|
+
"""有"当前值"的主题。
|
|
910
|
+
|
|
911
|
+
每个新订阅者会立即收到当前最新的值。
|
|
912
|
+
适合用于表示应用状态、配置值等。
|
|
913
|
+
"""
|
|
914
|
+
|
|
915
|
+
def __init__(self, initial_value):
|
|
916
|
+
"""创建一个新的 BehaviorSubject,指定初始值。"""
|
|
917
|
+
if _USE_RUST:
|
|
918
|
+
try:
|
|
919
|
+
self._inner = _rust_mod.BehaviorSubject(initial_value)
|
|
920
|
+
except Exception:
|
|
921
|
+
self._inner = _PyBehaviorSubject(initial_value)
|
|
922
|
+
else:
|
|
923
|
+
self._inner = _PyBehaviorSubject(initial_value)
|
|
924
|
+
|
|
925
|
+
def on_next(self, value):
|
|
926
|
+
"""发射一个值,并更新"当前值"。"""
|
|
927
|
+
if hasattr(self._inner, "on_next"):
|
|
928
|
+
self._inner.on_next(value)
|
|
929
|
+
|
|
930
|
+
def on_completed(self):
|
|
931
|
+
"""发出完成信号。"""
|
|
932
|
+
if hasattr(self._inner, "on_completed"):
|
|
933
|
+
self._inner.on_completed()
|
|
934
|
+
|
|
935
|
+
def subscribe(self, on_next=None, on_error=None, on_completed=None):
|
|
936
|
+
"""订阅,立即收到当前最新值。"""
|
|
937
|
+
next_cb = on_next if on_next is not None else (lambda v: None)
|
|
938
|
+
rust_sub = self._inner.subscribe(next_cb)
|
|
939
|
+
if isinstance(rust_sub, Subscription):
|
|
940
|
+
return rust_sub
|
|
941
|
+
return Subscription(rust_sub)
|
|
942
|
+
|
|
943
|
+
@property
|
|
944
|
+
def value(self):
|
|
945
|
+
"""返回当前值。"""
|
|
946
|
+
if hasattr(self._inner, "value"):
|
|
947
|
+
v = self._inner.value
|
|
948
|
+
if callable(v):
|
|
949
|
+
return v()
|
|
950
|
+
return v
|
|
951
|
+
if hasattr(self._inner, "_current"):
|
|
952
|
+
return self._inner._current
|
|
953
|
+
return None
|
|
954
|
+
|
|
955
|
+
def __repr__(self):
|
|
956
|
+
return "BehaviorSubject()"
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
class ReplaySubject:
|
|
960
|
+
"""可重放历史值的主题。
|
|
961
|
+
|
|
962
|
+
新订阅者会收到最近 capacity 个值的重放,以及后续的值。
|
|
963
|
+
适合用于缓存历史事件、聊天消息等场景。
|
|
964
|
+
"""
|
|
965
|
+
|
|
966
|
+
def __init__(self, capacity):
|
|
967
|
+
"""创建一个新的 ReplaySubject,指定缓冲区大小。"""
|
|
968
|
+
if _USE_RUST:
|
|
969
|
+
try:
|
|
970
|
+
self._inner = _rust_mod.ReplaySubject(int(capacity))
|
|
971
|
+
except Exception:
|
|
972
|
+
self._inner = _PyReplaySubject(capacity)
|
|
973
|
+
else:
|
|
974
|
+
self._inner = _PyReplaySubject(capacity)
|
|
975
|
+
|
|
976
|
+
def on_next(self, value):
|
|
977
|
+
"""发射一个值,并将其加入缓存。"""
|
|
978
|
+
if hasattr(self._inner, "on_next"):
|
|
979
|
+
self._inner.on_next(value)
|
|
980
|
+
|
|
981
|
+
def on_completed(self):
|
|
982
|
+
"""发出完成信号。"""
|
|
983
|
+
if hasattr(self._inner, "on_completed"):
|
|
984
|
+
self._inner.on_completed()
|
|
985
|
+
|
|
986
|
+
def subscribe(self, on_next=None, on_error=None, on_completed=None):
|
|
987
|
+
"""订阅,会先收到缓存的历史值重放。"""
|
|
988
|
+
next_cb = on_next if on_next is not None else (lambda v: None)
|
|
989
|
+
rust_sub = self._inner.subscribe(next_cb)
|
|
990
|
+
if isinstance(rust_sub, Subscription):
|
|
991
|
+
return rust_sub
|
|
992
|
+
return Subscription(rust_sub)
|
|
993
|
+
|
|
994
|
+
def __repr__(self):
|
|
995
|
+
return "ReplaySubject()"
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
# ============================================================================
|
|
999
|
+
# Scheduler - 调度器
|
|
1000
|
+
# ============================================================================
|
|
1001
|
+
|
|
1002
|
+
class CurrentThreadScheduler:
|
|
1003
|
+
"""当前线程调度器(同步执行)。"""
|
|
1004
|
+
|
|
1005
|
+
def __init__(self):
|
|
1006
|
+
if _USE_RUST:
|
|
1007
|
+
try:
|
|
1008
|
+
self._inner = _rust_mod.CurrentThreadScheduler()
|
|
1009
|
+
except Exception:
|
|
1010
|
+
self._inner = _PyCurrentThreadScheduler()
|
|
1011
|
+
else:
|
|
1012
|
+
self._inner = _PyCurrentThreadScheduler()
|
|
1013
|
+
|
|
1014
|
+
def now(self):
|
|
1015
|
+
return self._inner.now()
|
|
1016
|
+
|
|
1017
|
+
def __repr__(self):
|
|
1018
|
+
return "CurrentThreadScheduler()"
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
class ThreadPoolScheduler:
|
|
1022
|
+
"""线程池调度器(并发执行)。"""
|
|
1023
|
+
|
|
1024
|
+
def __init__(self, workers):
|
|
1025
|
+
if _USE_RUST:
|
|
1026
|
+
try:
|
|
1027
|
+
self._inner = _rust_mod.ThreadPoolScheduler(int(workers))
|
|
1028
|
+
except Exception:
|
|
1029
|
+
self._inner = _PyThreadPoolScheduler(workers)
|
|
1030
|
+
else:
|
|
1031
|
+
self._inner = _PyThreadPoolScheduler(workers)
|
|
1032
|
+
self._workers = int(workers)
|
|
1033
|
+
|
|
1034
|
+
def now(self):
|
|
1035
|
+
return self._inner.now()
|
|
1036
|
+
|
|
1037
|
+
def get_num_threads(self):
|
|
1038
|
+
if hasattr(self._inner, "get_num_threads"):
|
|
1039
|
+
return self._inner.get_num_threads()
|
|
1040
|
+
return self._workers
|
|
1041
|
+
|
|
1042
|
+
def __repr__(self):
|
|
1043
|
+
return f"ThreadPoolScheduler(workers={self._workers})"
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
class AsyncScheduler:
|
|
1047
|
+
"""异步调度器。"""
|
|
1048
|
+
|
|
1049
|
+
def __init__(self):
|
|
1050
|
+
if _USE_RUST:
|
|
1051
|
+
try:
|
|
1052
|
+
self._inner = _rust_mod.AsyncScheduler()
|
|
1053
|
+
except Exception:
|
|
1054
|
+
self._inner = _PyAsyncScheduler()
|
|
1055
|
+
else:
|
|
1056
|
+
self._inner = _PyAsyncScheduler()
|
|
1057
|
+
|
|
1058
|
+
def now(self):
|
|
1059
|
+
return self._inner.now()
|
|
1060
|
+
|
|
1061
|
+
def __repr__(self):
|
|
1062
|
+
return "AsyncScheduler()"
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
class ImmediateScheduler:
|
|
1066
|
+
"""立即调度器。"""
|
|
1067
|
+
|
|
1068
|
+
def __init__(self):
|
|
1069
|
+
if _USE_RUST:
|
|
1070
|
+
try:
|
|
1071
|
+
self._inner = _rust_mod.ImmediateScheduler()
|
|
1072
|
+
except Exception:
|
|
1073
|
+
self._inner = _PyImmediateScheduler()
|
|
1074
|
+
else:
|
|
1075
|
+
self._inner = _PyImmediateScheduler()
|
|
1076
|
+
|
|
1077
|
+
def now(self):
|
|
1078
|
+
return self._inner.now()
|
|
1079
|
+
|
|
1080
|
+
def __repr__(self):
|
|
1081
|
+
return "ImmediateScheduler()"
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
# ============================================================================
|
|
1085
|
+
# 导出符号
|
|
1086
|
+
# ============================================================================
|
|
1087
|
+
|
|
1088
|
+
__all__ = [
|
|
1089
|
+
"Observable",
|
|
1090
|
+
"Subscription",
|
|
1091
|
+
"PublishSubject",
|
|
1092
|
+
"BehaviorSubject",
|
|
1093
|
+
"ReplaySubject",
|
|
1094
|
+
"CurrentThreadScheduler",
|
|
1095
|
+
"ThreadPoolScheduler",
|
|
1096
|
+
"AsyncScheduler",
|
|
1097
|
+
"ImmediateScheduler",
|
|
1098
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rx-rust
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Rx-Rust — Reactive Extensions for Python. A high-performance reactive programming library with Observable/Observer/Subject/Scheduler support. (Pure-Python implementation: no Rust toolchain required to install.)
|
|
5
|
+
Author-email: Victor <victor@rx-rust.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitcode.com/VictorTop/Rx-Rust
|
|
8
|
+
Project-URL: Repository, https://gitcode.com/VictorTop/Rx-Rust.git
|
|
9
|
+
Project-URL: Issues, https://gitcode.com/VictorTop/Rx-Rust/issues
|
|
10
|
+
Keywords: reactive,rx,observable,streams,async,event-driven,functional-programming,publisher-subscriber,subject,scheduler,rx-rust,rxpy
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# rx-rust — Reactive Extensions for Python
|
|
29
|
+
|
|
30
|
+
一个高性能的响应式编程库,提供 Observable / Observer / Subject / Scheduler 支持。
|
|
31
|
+
|
|
32
|
+
## 快速开始
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install rx-rust
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import rx_rust
|
|
40
|
+
|
|
41
|
+
result = []
|
|
42
|
+
rx_rust.Observable.from_iter([1, 2, 3, 4, 5]) \
|
|
43
|
+
.filter(lambda x: x % 2 == 0) \
|
|
44
|
+
.map(lambda x: x * 10) \
|
|
45
|
+
.subscribe(on_next=lambda v: result.append(v))
|
|
46
|
+
|
|
47
|
+
print(result) # [20, 40]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 核心对象
|
|
51
|
+
|
|
52
|
+
- **Observable** — 可观察序列(`from_iter`, `of`, `range`, `repeat`, `empty`, `never`)
|
|
53
|
+
- **操作符** — `map`, `filter`, `take`, `skip`, `reduce`, `scan`, `flat_map`, `merge`, `concat`, `start_with`, `default_if_empty`, `contains`, `all`, `sum`, `count`, `do_on_next`
|
|
54
|
+
- **Subject** — `PublishSubject`, `BehaviorSubject`, `ReplaySubject`
|
|
55
|
+
- **Subscription** — 订阅管理,支持 `dispose()` 和 `is_disposed()`
|
|
56
|
+
- **Scheduler** — `CurrentThreadScheduler`, `ThreadPoolScheduler`, `AsyncScheduler`, `ImmediateScheduler`
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
rx_rust/__init__.py,sha256=Q3A6QP5OrF2P7rvpUgbg-ToD89rYDjkiMFOZy9lTqRE,35173
|
|
2
|
+
rx_rust-0.1.0.dist-info/METADATA,sha256=1y_cBNWhkTHBCQ4cba91RYvUGQzYzZB0f8hq-iOtsd0,2499
|
|
3
|
+
rx_rust-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
4
|
+
rx_rust-0.1.0.dist-info/top_level.txt,sha256=yYXBLXgtBrVdSQ1mAw4CVG_Pjn7kdOHUK3LClXvIsX0,8
|
|
5
|
+
rx_rust-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rx_rust
|