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 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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ rx_rust