reactivex 4.1.0__py3-none-any.whl → 5.0.0a2__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.
- reactivex/__init__.py +35 -39
- reactivex/_version.py +1 -1
- reactivex/abc/disposable.py +3 -4
- reactivex/abc/observable.py +13 -6
- reactivex/abc/observer.py +2 -1
- reactivex/abc/periodicscheduler.py +7 -6
- reactivex/abc/scheduler.py +10 -9
- reactivex/abc/subject.py +5 -5
- reactivex/disposable/compositedisposable.py +4 -4
- reactivex/disposable/disposable.py +1 -2
- reactivex/disposable/multipleassignmentdisposable.py +2 -3
- reactivex/disposable/refcountdisposable.py +1 -2
- reactivex/disposable/serialdisposable.py +4 -5
- reactivex/disposable/singleassignmentdisposable.py +3 -4
- reactivex/internal/__init__.py +2 -0
- reactivex/internal/basic.py +2 -2
- reactivex/internal/concurrency.py +2 -1
- reactivex/internal/curry.py +59 -0
- reactivex/internal/exceptions.py +7 -12
- reactivex/internal/priorityqueue.py +2 -2
- reactivex/internal/utils.py +3 -2
- reactivex/notification.py +22 -21
- reactivex/observable/case.py +5 -6
- reactivex/observable/catch.py +3 -2
- reactivex/observable/combinelatest.py +4 -5
- reactivex/observable/concat.py +3 -2
- reactivex/observable/connectableobservable.py +7 -7
- reactivex/observable/defer.py +4 -3
- reactivex/observable/empty.py +3 -4
- reactivex/observable/forkjoin.py +5 -5
- reactivex/observable/fromcallback.py +4 -3
- reactivex/observable/fromfuture.py +2 -2
- reactivex/observable/fromiterable.py +4 -3
- reactivex/observable/generate.py +2 -2
- reactivex/observable/generatewithrelativetime.py +4 -3
- reactivex/observable/groupedobservable.py +4 -4
- reactivex/observable/ifthen.py +3 -2
- reactivex/observable/interval.py +1 -4
- reactivex/observable/marbles.py +18 -17
- reactivex/observable/mixins/__init__.py +32 -0
- reactivex/observable/mixins/combination.py +481 -0
- reactivex/observable/mixins/conditional.py +135 -0
- reactivex/observable/mixins/error_handling.py +130 -0
- reactivex/observable/mixins/filtering.py +1119 -0
- reactivex/observable/mixins/mathematical.py +277 -0
- reactivex/observable/mixins/multicasting.py +306 -0
- reactivex/observable/mixins/testing.py +193 -0
- reactivex/observable/mixins/time_based.py +209 -0
- reactivex/observable/mixins/transformation.py +632 -0
- reactivex/observable/mixins/utility.py +811 -0
- reactivex/observable/mixins/windowing.py +688 -0
- reactivex/observable/never.py +2 -2
- reactivex/observable/observable.py +72 -25
- reactivex/observable/onerrorresumenext.py +7 -6
- reactivex/observable/range.py +6 -6
- reactivex/observable/repeat.py +2 -2
- reactivex/observable/returnvalue.py +6 -5
- reactivex/observable/start.py +3 -2
- reactivex/observable/startasync.py +2 -1
- reactivex/observable/throw.py +3 -3
- reactivex/observable/timer.py +12 -12
- reactivex/observable/toasync.py +3 -2
- reactivex/observable/using.py +5 -4
- reactivex/observable/withlatestfrom.py +4 -5
- reactivex/observable/zip.py +7 -6
- reactivex/observer/autodetachobserver.py +4 -4
- reactivex/observer/observer.py +5 -4
- reactivex/observer/scheduledobserver.py +2 -2
- reactivex/operators/__init__.py +162 -208
- reactivex/operators/_all.py +23 -6
- reactivex/operators/_amb.py +88 -75
- reactivex/operators/_asobservable.py +20 -17
- reactivex/operators/_average.py +48 -45
- reactivex/operators/_buffer.py +81 -35
- reactivex/operators/_bufferwithtime.py +29 -9
- reactivex/operators/_bufferwithtimeorcount.py +27 -8
- reactivex/operators/_catch.py +33 -32
- reactivex/operators/_combinelatest.py +28 -20
- reactivex/operators/_concat.py +16 -13
- reactivex/operators/_contains.py +25 -6
- reactivex/operators/_count.py +24 -8
- reactivex/operators/_debounce.py +141 -138
- reactivex/operators/_defaultifempty.py +45 -42
- reactivex/operators/_delay.py +24 -23
- reactivex/operators/_delaysubscription.py +23 -21
- reactivex/operators/_delaywithmapper.py +10 -11
- reactivex/operators/_dematerialize.py +25 -21
- reactivex/operators/_distinct.py +50 -46
- reactivex/operators/_distinctuntilchanged.py +60 -57
- reactivex/operators/_do.py +123 -116
- reactivex/operators/_dowhile.py +3 -2
- reactivex/operators/_elementatordefault.py +57 -33
- reactivex/operators/_exclusive.py +59 -53
- reactivex/operators/_expand.py +82 -77
- reactivex/operators/_filter.py +63 -68
- reactivex/operators/_finallyaction.py +3 -2
- reactivex/operators/_find.py +49 -32
- reactivex/operators/_first.py +18 -11
- reactivex/operators/_firstordefault.py +5 -4
- reactivex/operators/_flatmap.py +89 -83
- reactivex/operators/_forkjoin.py +23 -18
- reactivex/operators/_groupby.py +27 -6
- reactivex/operators/_groupbyuntil.py +8 -5
- reactivex/operators/_groupjoin.py +7 -6
- reactivex/operators/_ignoreelements.py +20 -15
- reactivex/operators/_isempty.py +15 -4
- reactivex/operators/_join.py +6 -5
- reactivex/operators/_last.py +36 -31
- reactivex/operators/_lastordefault.py +8 -8
- reactivex/operators/_map.py +54 -39
- reactivex/operators/_materialize.py +30 -31
- reactivex/operators/_max.py +18 -11
- reactivex/operators/_maxby.py +5 -5
- reactivex/operators/_merge.py +132 -129
- reactivex/operators/_min.py +16 -10
- reactivex/operators/_minby.py +9 -8
- reactivex/operators/_multicast.py +9 -9
- reactivex/operators/_observeon.py +35 -31
- reactivex/operators/_onerrorresumenext.py +2 -1
- reactivex/operators/_pairwise.py +38 -34
- reactivex/operators/_partition.py +80 -73
- reactivex/operators/_pluck.py +4 -3
- reactivex/operators/_publish.py +36 -21
- reactivex/operators/_publishvalue.py +8 -7
- reactivex/operators/_reduce.py +16 -12
- reactivex/operators/_repeat.py +33 -30
- reactivex/operators/_replay.py +9 -9
- reactivex/operators/_retry.py +12 -10
- reactivex/operators/_sample.py +31 -27
- reactivex/operators/_scan.py +41 -39
- reactivex/operators/_sequenceequal.py +8 -7
- reactivex/operators/_single.py +20 -13
- reactivex/operators/_singleordefault.py +6 -5
- reactivex/operators/_skip.py +35 -32
- reactivex/operators/_skiplast.py +38 -34
- reactivex/operators/_skiplastwithtime.py +5 -4
- reactivex/operators/_skipuntil.py +40 -35
- reactivex/operators/_skipuntilwithtime.py +4 -3
- reactivex/operators/_skipwhile.py +65 -44
- reactivex/operators/_skipwithtime.py +50 -46
- reactivex/operators/_slice.py +58 -53
- reactivex/operators/_some.py +48 -47
- reactivex/operators/_startswith.py +17 -15
- reactivex/operators/_subscribeon.py +44 -41
- reactivex/operators/_sum.py +23 -6
- reactivex/operators/_switchlatest.py +71 -69
- reactivex/operators/_take.py +37 -33
- reactivex/operators/_takelast.py +37 -36
- reactivex/operators/_takelastbuffer.py +38 -37
- reactivex/operators/_takelastwithtime.py +60 -56
- reactivex/operators/_takeuntil.py +33 -32
- reactivex/operators/_takeuntilwithtime.py +42 -39
- reactivex/operators/_takewhile.py +108 -100
- reactivex/operators/_takewithtime.py +46 -41
- reactivex/operators/_throttlefirst.py +52 -45
- reactivex/operators/_timeinterval.py +40 -36
- reactivex/operators/_timeout.py +81 -79
- reactivex/operators/_timeoutwithmapper.py +6 -5
- reactivex/operators/_timestamp.py +24 -22
- reactivex/operators/_todict.py +51 -43
- reactivex/operators/_tofuture.py +24 -15
- reactivex/operators/_toiterable.py +33 -27
- reactivex/operators/_tomarbles.py +5 -5
- reactivex/operators/_toset.py +29 -19
- reactivex/operators/_whiledo.py +2 -1
- reactivex/operators/_window.py +100 -99
- reactivex/operators/_windowwithcount.py +56 -54
- reactivex/operators/_windowwithtime.py +95 -79
- reactivex/operators/_windowwithtimeorcount.py +85 -69
- reactivex/operators/_withlatestfrom.py +13 -9
- reactivex/operators/_zip.py +67 -63
- reactivex/operators/connectable/_refcount.py +4 -3
- reactivex/pipe.py +2 -1
- reactivex/run.py +8 -4
- reactivex/scheduler/catchscheduler.py +11 -10
- reactivex/scheduler/currentthreadscheduler.py +2 -3
- reactivex/scheduler/eventloop/asyncioscheduler.py +7 -6
- reactivex/scheduler/eventloop/asynciothreadsafescheduler.py +12 -14
- reactivex/scheduler/eventloop/eventletscheduler.py +4 -4
- reactivex/scheduler/eventloop/geventscheduler.py +4 -4
- reactivex/scheduler/eventloop/ioloopscheduler.py +4 -4
- reactivex/scheduler/eventloop/twistedscheduler.py +4 -4
- reactivex/scheduler/eventloopscheduler.py +9 -12
- reactivex/scheduler/historicalscheduler.py +1 -2
- reactivex/scheduler/immediatescheduler.py +5 -4
- reactivex/scheduler/mainloop/gtkscheduler.py +6 -7
- reactivex/scheduler/mainloop/pygamescheduler.py +4 -4
- reactivex/scheduler/mainloop/qtscheduler.py +6 -6
- reactivex/scheduler/mainloop/tkinterscheduler.py +4 -4
- reactivex/scheduler/mainloop/wxscheduler.py +7 -7
- reactivex/scheduler/newthreadscheduler.py +6 -8
- reactivex/scheduler/periodicscheduler.py +4 -4
- reactivex/scheduler/scheduleditem.py +4 -4
- reactivex/scheduler/scheduler.py +5 -5
- reactivex/scheduler/threadpoolscheduler.py +3 -3
- reactivex/scheduler/timeoutscheduler.py +5 -4
- reactivex/scheduler/trampoline.py +1 -2
- reactivex/scheduler/trampolinescheduler.py +5 -6
- reactivex/scheduler/virtualtimescheduler.py +4 -4
- reactivex/subject/asyncsubject.py +2 -2
- reactivex/subject/behaviorsubject.py +2 -2
- reactivex/subject/innersubscription.py +2 -2
- reactivex/subject/replaysubject.py +8 -8
- reactivex/subject/subject.py +4 -4
- reactivex/testing/coldobservable.py +5 -5
- reactivex/testing/hotobservable.py +6 -6
- reactivex/testing/marbles.py +21 -20
- reactivex/testing/mockdisposable.py +1 -3
- reactivex/testing/mockobserver.py +2 -2
- reactivex/testing/reactivetest.py +2 -2
- reactivex/testing/recorded.py +1 -1
- reactivex/testing/subscription.py +3 -3
- reactivex/testing/testscheduler.py +13 -12
- reactivex/typing.py +25 -14
- {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/METADATA +59 -26
- reactivex-5.0.0a2.dist-info/RECORD +236 -0
- {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/WHEEL +1 -1
- reactivex-4.1.0.dist-info/RECORD +0 -223
- {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TypeVar, cast
|
|
2
2
|
|
|
3
3
|
from .. import abc
|
|
4
4
|
from ..disposable import Disposable
|
|
@@ -25,7 +25,7 @@ class AsyncSubject(Subject[_T]):
|
|
|
25
25
|
def _subscribe_core(
|
|
26
26
|
self,
|
|
27
27
|
observer: abc.ObserverBase[_T],
|
|
28
|
-
scheduler:
|
|
28
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
29
29
|
) -> abc.DisposableBase:
|
|
30
30
|
with self.lock:
|
|
31
31
|
self.check_disposed()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TypeVar, cast
|
|
2
2
|
|
|
3
3
|
from .. import abc
|
|
4
4
|
from ..disposable import Disposable
|
|
@@ -31,7 +31,7 @@ class BehaviorSubject(Subject[_T]):
|
|
|
31
31
|
def _subscribe_core(
|
|
32
32
|
self,
|
|
33
33
|
observer: abc.ObserverBase[_T],
|
|
34
|
-
scheduler:
|
|
34
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
35
35
|
) -> abc.DisposableBase:
|
|
36
36
|
with self.lock:
|
|
37
37
|
self.check_disposed()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import threading
|
|
2
|
-
from typing import TYPE_CHECKING,
|
|
2
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
3
3
|
|
|
4
4
|
from .. import abc
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ _T = TypeVar("_T")
|
|
|
11
11
|
|
|
12
12
|
class InnerSubscription(abc.DisposableBase):
|
|
13
13
|
def __init__(
|
|
14
|
-
self, subject: "Subject[_T]", observer:
|
|
14
|
+
self, subject: "Subject[_T]", observer: abc.ObserverBase[_T] | None = None
|
|
15
15
|
):
|
|
16
16
|
self.subject = subject
|
|
17
17
|
self.observer = observer
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from collections import deque
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, NamedTuple, TypeVar, cast
|
|
5
5
|
|
|
6
6
|
from reactivex.observer.scheduledobserver import ScheduledObserver
|
|
7
7
|
from reactivex.scheduler import CurrentThreadScheduler
|
|
@@ -37,9 +37,9 @@ class ReplaySubject(Subject[_T]):
|
|
|
37
37
|
|
|
38
38
|
def __init__(
|
|
39
39
|
self,
|
|
40
|
-
buffer_size:
|
|
41
|
-
window:
|
|
42
|
-
scheduler:
|
|
40
|
+
buffer_size: int | None = None,
|
|
41
|
+
window: typing.RelativeTime | None = None,
|
|
42
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
43
43
|
) -> None:
|
|
44
44
|
"""Initializes a new instance of the ReplaySubject class with
|
|
45
45
|
the specified buffer size, window and scheduler.
|
|
@@ -54,15 +54,15 @@ class ReplaySubject(Subject[_T]):
|
|
|
54
54
|
super().__init__()
|
|
55
55
|
self.buffer_size = sys.maxsize if buffer_size is None else buffer_size
|
|
56
56
|
self.scheduler = scheduler or CurrentThreadScheduler.singleton()
|
|
57
|
-
self.
|
|
57
|
+
self._window = (
|
|
58
58
|
timedelta.max if window is None else self.scheduler.to_timedelta(window)
|
|
59
59
|
)
|
|
60
|
-
self.queue:
|
|
60
|
+
self.queue: deque[QueueItem] = deque()
|
|
61
61
|
|
|
62
62
|
def _subscribe_core(
|
|
63
63
|
self,
|
|
64
64
|
observer: abc.ObserverBase[_T],
|
|
65
|
-
scheduler:
|
|
65
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
66
66
|
) -> abc.DisposableBase:
|
|
67
67
|
so = ScheduledObserver(self.scheduler, observer)
|
|
68
68
|
subscription = RemovableDisposable(self, so)
|
|
@@ -87,7 +87,7 @@ class ReplaySubject(Subject[_T]):
|
|
|
87
87
|
while len(self.queue) > self.buffer_size:
|
|
88
88
|
self.queue.popleft()
|
|
89
89
|
|
|
90
|
-
while self.queue and (now - self.queue[0].interval) > self.
|
|
90
|
+
while self.queue and (now - self.queue[0].interval) > self._window:
|
|
91
91
|
self.queue.popleft()
|
|
92
92
|
|
|
93
93
|
def _on_next_core(self, value: _T) -> None:
|
reactivex/subject/subject.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import threading
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import TypeVar
|
|
3
3
|
|
|
4
4
|
from .. import abc
|
|
5
5
|
from ..disposable import Disposable
|
|
@@ -21,8 +21,8 @@ class Subject(Observable[_T], Observer[_T], abc.SubjectBase[_T]):
|
|
|
21
21
|
super().__init__()
|
|
22
22
|
|
|
23
23
|
self.is_disposed = False
|
|
24
|
-
self.observers:
|
|
25
|
-
self.exception:
|
|
24
|
+
self.observers: list[abc.ObserverBase[_T]] = []
|
|
25
|
+
self.exception: Exception | None = None
|
|
26
26
|
|
|
27
27
|
self.lock = threading.RLock()
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ class Subject(Observable[_T], Observer[_T], abc.SubjectBase[_T]):
|
|
|
33
33
|
def _subscribe_core(
|
|
34
34
|
self,
|
|
35
35
|
observer: abc.ObserverBase[_T],
|
|
36
|
-
scheduler:
|
|
36
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
37
37
|
) -> abc.DisposableBase:
|
|
38
38
|
with self.lock:
|
|
39
39
|
self.check_disposed()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any,
|
|
1
|
+
from typing import Any, TypeVar
|
|
2
2
|
|
|
3
3
|
from reactivex import Notification, Observable, abc
|
|
4
4
|
from reactivex.disposable import CompositeDisposable, Disposable
|
|
@@ -12,18 +12,18 @@ _T = TypeVar("_T")
|
|
|
12
12
|
|
|
13
13
|
class ColdObservable(Observable[_T]):
|
|
14
14
|
def __init__(
|
|
15
|
-
self, scheduler: VirtualTimeScheduler, messages:
|
|
15
|
+
self, scheduler: VirtualTimeScheduler, messages: list[Recorded[_T]]
|
|
16
16
|
) -> None:
|
|
17
17
|
super().__init__()
|
|
18
18
|
|
|
19
19
|
self.scheduler = scheduler
|
|
20
20
|
self.messages = messages
|
|
21
|
-
self.subscriptions:
|
|
21
|
+
self.subscriptions: list[Subscription] = []
|
|
22
22
|
|
|
23
23
|
def _subscribe_core(
|
|
24
24
|
self,
|
|
25
|
-
observer:
|
|
26
|
-
scheduler:
|
|
25
|
+
observer: abc.ObserverBase[_T] | None = None,
|
|
26
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
27
27
|
) -> abc.DisposableBase:
|
|
28
28
|
self.subscriptions.append(Subscription(self.scheduler.clock))
|
|
29
29
|
index = len(self.subscriptions) - 1
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any,
|
|
1
|
+
from typing import Any, TypeVar
|
|
2
2
|
|
|
3
3
|
from reactivex import Observable, abc
|
|
4
4
|
from reactivex.disposable import Disposable
|
|
@@ -13,14 +13,14 @@ _T = TypeVar("_T")
|
|
|
13
13
|
|
|
14
14
|
class HotObservable(Observable[_T]):
|
|
15
15
|
def __init__(
|
|
16
|
-
self, scheduler: VirtualTimeScheduler, messages:
|
|
16
|
+
self, scheduler: VirtualTimeScheduler, messages: list[Recorded[_T]]
|
|
17
17
|
) -> None:
|
|
18
18
|
super().__init__()
|
|
19
19
|
|
|
20
20
|
self.scheduler = scheduler
|
|
21
21
|
self.messages = messages
|
|
22
|
-
self.subscriptions:
|
|
23
|
-
self.observers:
|
|
22
|
+
self.subscriptions: list[Subscription] = []
|
|
23
|
+
self.observers: list[abc.ObserverBase[_T]] = []
|
|
24
24
|
|
|
25
25
|
observable = self
|
|
26
26
|
|
|
@@ -43,8 +43,8 @@ class HotObservable(Observable[_T]):
|
|
|
43
43
|
|
|
44
44
|
def _subscribe_core(
|
|
45
45
|
self,
|
|
46
|
-
observer:
|
|
47
|
-
scheduler:
|
|
46
|
+
observer: abc.ObserverBase[_T] | None = None,
|
|
47
|
+
scheduler: abc.SchedulerBase | None = None,
|
|
48
48
|
) -> abc.DisposableBase:
|
|
49
49
|
if observer:
|
|
50
50
|
self.observers.append(observer)
|
reactivex/testing/marbles.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
1
2
|
from contextlib import contextmanager
|
|
2
|
-
from typing import Any,
|
|
3
|
+
from typing import Any, NamedTuple, cast
|
|
3
4
|
from warnings import warn
|
|
4
5
|
|
|
5
6
|
import reactivex
|
|
@@ -18,19 +19,19 @@ new_thread_scheduler = NewThreadScheduler()
|
|
|
18
19
|
|
|
19
20
|
class MarblesContext(NamedTuple):
|
|
20
21
|
start: Callable[
|
|
21
|
-
[
|
|
22
|
+
[Observable[Any] | Callable[[], Observable[Any]]], list[Recorded[Any]]
|
|
22
23
|
]
|
|
23
24
|
cold: Callable[
|
|
24
|
-
[str,
|
|
25
|
+
[str, dict[str | float, Any] | None, Exception | None],
|
|
25
26
|
Observable[Any],
|
|
26
27
|
]
|
|
27
28
|
hot: Callable[
|
|
28
|
-
[str,
|
|
29
|
+
[str, dict[str | float, Any] | None, Exception | None],
|
|
29
30
|
Observable[Any],
|
|
30
31
|
]
|
|
31
32
|
exp: Callable[
|
|
32
|
-
[str,
|
|
33
|
-
|
|
33
|
+
[str, dict[str | float, Any] | None, Exception | None],
|
|
34
|
+
list[Recorded[Any]],
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
|
|
@@ -97,13 +98,13 @@ def marbles_testing(
|
|
|
97
98
|
)
|
|
98
99
|
|
|
99
100
|
def test_start(
|
|
100
|
-
create:
|
|
101
|
-
) ->
|
|
101
|
+
create: Observable[Any] | Callable[[], Observable[Any]],
|
|
102
|
+
) -> list[Recorded[Any]]:
|
|
102
103
|
nonlocal start_called
|
|
103
104
|
check()
|
|
104
105
|
|
|
105
106
|
if isinstance(create, Observable):
|
|
106
|
-
create_ = create
|
|
107
|
+
create_ = cast(Observable[Any], create)
|
|
107
108
|
|
|
108
109
|
def default_create() -> Observable[Any]:
|
|
109
110
|
return create_
|
|
@@ -123,9 +124,9 @@ def marbles_testing(
|
|
|
123
124
|
|
|
124
125
|
def test_expected(
|
|
125
126
|
string: str,
|
|
126
|
-
lookup:
|
|
127
|
-
error:
|
|
128
|
-
) ->
|
|
127
|
+
lookup: dict[str | float, Any] | None = None,
|
|
128
|
+
error: Exception | None = None,
|
|
129
|
+
) -> list[Recorded[Any]]:
|
|
129
130
|
messages = parse(
|
|
130
131
|
string,
|
|
131
132
|
timespan=timespan,
|
|
@@ -137,8 +138,8 @@ def marbles_testing(
|
|
|
137
138
|
|
|
138
139
|
def test_cold(
|
|
139
140
|
string: str,
|
|
140
|
-
lookup:
|
|
141
|
-
error:
|
|
141
|
+
lookup: dict[str | float, Any] | None = None,
|
|
142
|
+
error: Exception | None = None,
|
|
142
143
|
) -> Observable[Any]:
|
|
143
144
|
check()
|
|
144
145
|
return reactivex.from_marbles(
|
|
@@ -150,8 +151,8 @@ def marbles_testing(
|
|
|
150
151
|
|
|
151
152
|
def test_hot(
|
|
152
153
|
string: str,
|
|
153
|
-
lookup:
|
|
154
|
-
error:
|
|
154
|
+
lookup: dict[str | float, Any] | None = None,
|
|
155
|
+
error: Exception | None = None,
|
|
155
156
|
) -> Observable[Any]:
|
|
156
157
|
check()
|
|
157
158
|
hot_obs: Observable[Any] = reactivex.hot(
|
|
@@ -171,17 +172,17 @@ def marbles_testing(
|
|
|
171
172
|
|
|
172
173
|
|
|
173
174
|
def messages_to_records(
|
|
174
|
-
messages:
|
|
175
|
-
) ->
|
|
175
|
+
messages: list[tuple[typing.RelativeTime, Notification[Any]]],
|
|
176
|
+
) -> list[Recorded[Any]]:
|
|
176
177
|
"""
|
|
177
178
|
Helper function to convert messages returned by parse() to a list of
|
|
178
179
|
Recorded.
|
|
179
180
|
"""
|
|
180
|
-
records:
|
|
181
|
+
records: list[Recorded[Any]] = []
|
|
181
182
|
|
|
182
183
|
for message in messages:
|
|
183
184
|
time, notification = message
|
|
184
|
-
if isinstance(time, (int
|
|
185
|
+
if isinstance(time, (int | float)):
|
|
185
186
|
time_ = int(time)
|
|
186
187
|
else:
|
|
187
188
|
time_ = time.microseconds // 1000
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from reactivex import abc, typing
|
|
4
2
|
from reactivex.scheduler import VirtualTimeScheduler
|
|
5
3
|
|
|
@@ -7,7 +5,7 @@ from reactivex.scheduler import VirtualTimeScheduler
|
|
|
7
5
|
class MockDisposable(abc.DisposableBase):
|
|
8
6
|
def __init__(self, scheduler: VirtualTimeScheduler):
|
|
9
7
|
self.scheduler = scheduler
|
|
10
|
-
self.disposes:
|
|
8
|
+
self.disposes: list[typing.AbsoluteTime] = []
|
|
11
9
|
self.disposes.append(self.scheduler.clock)
|
|
12
10
|
|
|
13
11
|
def dispose(self) -> None:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import TypeVar
|
|
2
2
|
|
|
3
3
|
from reactivex import abc
|
|
4
4
|
from reactivex.notification import OnCompleted, OnError, OnNext
|
|
@@ -12,7 +12,7 @@ _T = TypeVar("_T")
|
|
|
12
12
|
class MockObserver(abc.ObserverBase[_T]):
|
|
13
13
|
def __init__(self, scheduler: VirtualTimeScheduler) -> None:
|
|
14
14
|
self.scheduler = scheduler
|
|
15
|
-
self.messages:
|
|
15
|
+
self.messages: list[Recorded[_T]] = []
|
|
16
16
|
|
|
17
17
|
def on_next(self, value: _T) -> None:
|
|
18
18
|
self.messages.append(Recorded(self.scheduler.clock, OnNext(value)))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import math
|
|
2
2
|
import types
|
|
3
|
-
from typing import Any, Generic, TypeVar
|
|
3
|
+
from typing import Any, Generic, TypeVar
|
|
4
4
|
|
|
5
5
|
from reactivex import typing
|
|
6
6
|
from reactivex.notification import OnCompleted, OnError, OnNext
|
|
@@ -68,7 +68,7 @@ class ReactiveTest:
|
|
|
68
68
|
|
|
69
69
|
@staticmethod
|
|
70
70
|
def on_error(
|
|
71
|
-
ticks: int, error:
|
|
71
|
+
ticks: int, error: Exception | str | types.FunctionType
|
|
72
72
|
) -> Recorded[Any]:
|
|
73
73
|
if isinstance(error, types.FunctionType):
|
|
74
74
|
return Recorded(ticks, OnErrorPredicate(error))
|
reactivex/testing/recorded.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Subscription:
|
|
6
|
-
def __init__(self, start: int, end:
|
|
6
|
+
def __init__(self, start: int, end: int | None = None):
|
|
7
7
|
self.subscribe = start
|
|
8
8
|
self.unsubscribe = end or sys.maxsize
|
|
9
9
|
|
|
@@ -22,4 +22,4 @@ class Subscription:
|
|
|
22
22
|
unsubscribe = (
|
|
23
23
|
"Infinite" if self.unsubscribe == sys.maxsize else self.unsubscribe
|
|
24
24
|
)
|
|
25
|
-
return "(
|
|
25
|
+
return f"({self.subscribe}, {unsubscribe})"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any, TypeVar, cast
|
|
2
3
|
|
|
3
4
|
import reactivex
|
|
4
5
|
from reactivex import Observable, abc, typing
|
|
@@ -46,10 +47,10 @@ class TestScheduler(VirtualTimeScheduler):
|
|
|
46
47
|
|
|
47
48
|
def start(
|
|
48
49
|
self,
|
|
49
|
-
create:
|
|
50
|
-
created:
|
|
51
|
-
subscribed:
|
|
52
|
-
disposed:
|
|
50
|
+
create: Callable[[], Observable[_T]] | None = None,
|
|
51
|
+
created: float | None = None,
|
|
52
|
+
subscribed: float | None = None,
|
|
53
|
+
disposed: float | None = None,
|
|
53
54
|
) -> MockObserver[_T]:
|
|
54
55
|
"""Starts the test scheduler and uses the specified virtual
|
|
55
56
|
times to invoke the factory function, subscribe to the
|
|
@@ -76,8 +77,8 @@ class TestScheduler(VirtualTimeScheduler):
|
|
|
76
77
|
disposed = disposed or ReactiveTest.disposed
|
|
77
78
|
|
|
78
79
|
observer = self.create_observer()
|
|
79
|
-
subscription:
|
|
80
|
-
source:
|
|
80
|
+
subscription: abc.DisposableBase | None = None
|
|
81
|
+
source: abc.ObservableBase[_T] | None = None
|
|
81
82
|
|
|
82
83
|
def action_create(
|
|
83
84
|
scheduler: abc.SchedulerBase, state: Any = None
|
|
@@ -114,7 +115,7 @@ class TestScheduler(VirtualTimeScheduler):
|
|
|
114
115
|
return observer
|
|
115
116
|
|
|
116
117
|
def create_hot_observable(
|
|
117
|
-
self, *args:
|
|
118
|
+
self, *args: Recorded[_T] | list[Recorded[_T]]
|
|
118
119
|
) -> HotObservable[_T]:
|
|
119
120
|
"""Creates a hot observable using the specified timestamped
|
|
120
121
|
notification messages either as a list or by multiple arguments.
|
|
@@ -127,14 +128,14 @@ class TestScheduler(VirtualTimeScheduler):
|
|
|
127
128
|
of subscriptions and notifications.
|
|
128
129
|
"""
|
|
129
130
|
|
|
130
|
-
if args and isinstance(args[0],
|
|
131
|
+
if args and isinstance(args[0], list):
|
|
131
132
|
messages = args[0]
|
|
132
133
|
else:
|
|
133
|
-
messages = cast(
|
|
134
|
+
messages = cast(list[Recorded[_T]], list(args))
|
|
134
135
|
return HotObservable(self, messages)
|
|
135
136
|
|
|
136
137
|
def create_cold_observable(
|
|
137
|
-
self, *args:
|
|
138
|
+
self, *args: Recorded[_T] | list[Recorded[_T]]
|
|
138
139
|
) -> ColdObservable[_T]:
|
|
139
140
|
"""Creates a cold observable using the specified timestamped
|
|
140
141
|
notification messages either as an array or arguments.
|
|
@@ -152,7 +153,7 @@ class TestScheduler(VirtualTimeScheduler):
|
|
|
152
153
|
if args and isinstance(args[0], list):
|
|
153
154
|
messages = args[0]
|
|
154
155
|
else:
|
|
155
|
-
messages = cast(
|
|
156
|
+
messages = cast(list[Recorded[_T]], list(args))
|
|
156
157
|
return ColdObservable(self, messages)
|
|
157
158
|
|
|
158
159
|
def create_observer(self) -> MockObserver[Any]:
|
reactivex/typing.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
1
2
|
from threading import Thread
|
|
2
|
-
from typing import
|
|
3
|
+
from typing import TypeAlias, TypeVar
|
|
4
|
+
|
|
5
|
+
from typing_extensions import TypeAliasType
|
|
3
6
|
|
|
4
7
|
from .abc.observable import Subscription
|
|
5
8
|
from .abc.observer import OnCompleted, OnError, OnNext
|
|
@@ -19,20 +22,28 @@ _TState = TypeVar("_TState")
|
|
|
19
22
|
_T1 = TypeVar("_T1")
|
|
20
23
|
_T2 = TypeVar("_T2")
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
PredicateIndexed = Callable[[_T1, int], bool]
|
|
28
|
-
Comparer = Callable[[_T1, _T1], bool]
|
|
29
|
-
SubComparer = Callable[[_T1, _T1], int]
|
|
30
|
-
Accumulator = Callable[[_TState, _T1], _TState]
|
|
25
|
+
# Non-generic type aliases
|
|
26
|
+
Action: TypeAlias = Callable[[], None]
|
|
27
|
+
Startable: TypeAlias = StartableBase | Thread
|
|
28
|
+
StartableTarget: TypeAlias = Callable[..., None]
|
|
29
|
+
StartableFactory: TypeAlias = Callable[[StartableTarget], Startable]
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
# Generic type aliases
|
|
32
|
+
Mapper = TypeAliasType("Mapper", Callable[[_T1], _T2], type_params=(_T1, _T2))
|
|
33
|
+
MapperIndexed = TypeAliasType(
|
|
34
|
+
"MapperIndexed", Callable[[_T1, int], _T2], type_params=(_T1, _T2)
|
|
35
|
+
)
|
|
36
|
+
Predicate = TypeAliasType("Predicate", Callable[[_T1], bool], type_params=(_T1,))
|
|
37
|
+
PredicateIndexed = TypeAliasType(
|
|
38
|
+
"PredicateIndexed", Callable[[_T1, int], bool], type_params=(_T1,)
|
|
39
|
+
)
|
|
40
|
+
Comparer = TypeAliasType("Comparer", Callable[[_T1, _T1], bool], type_params=(_T1,))
|
|
41
|
+
SubComparer = TypeAliasType(
|
|
42
|
+
"SubComparer", Callable[[_T1, _T1], int], type_params=(_T1,)
|
|
43
|
+
)
|
|
44
|
+
Accumulator = TypeAliasType(
|
|
45
|
+
"Accumulator", Callable[[_TState, _T1], _TState], type_params=(_TState, _T1)
|
|
46
|
+
)
|
|
36
47
|
|
|
37
48
|
__all__ = [
|
|
38
49
|
"Accumulator",
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactivex
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0a2
|
|
4
4
|
Summary: ReactiveX (Rx) for Python
|
|
5
|
-
|
|
5
|
+
Project-URL: Homepage, http://reactivex.io
|
|
6
|
+
Project-URL: Repository, https://github.com/ReactiveX/RxPY
|
|
7
|
+
Project-URL: Documentation, https://rxpy.readthedocs.io/en/latest/
|
|
8
|
+
Author-email: Dag Brattli <dag@brattli.net>
|
|
9
|
+
License-Expression: MIT
|
|
6
10
|
License-File: LICENSE
|
|
7
|
-
Author: Dag Brattli
|
|
8
|
-
Author-email: dag@brattli.net
|
|
9
|
-
Requires-Python: >=3.8,<4.0
|
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: Environment :: Other Environment
|
|
12
13
|
Classifier: Intended Audience :: Developers
|
|
13
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
15
|
Classifier: Operating System :: OS Independent
|
|
15
16
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,10 +21,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.14
|
|
23
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
-
Requires-
|
|
26
|
-
|
|
27
|
-
Project-URL: Homepage, http://reactivex.io
|
|
28
|
-
Project-URL: Repository, https://github.com/ReactiveX/RxPY
|
|
24
|
+
Requires-Python: <4.0,>=3.10
|
|
25
|
+
Requires-Dist: typing-extensions<5,>=4.15.0
|
|
29
26
|
Description-Content-Type: text/x-rst
|
|
30
27
|
|
|
31
28
|
===============================
|
|
@@ -89,6 +86,43 @@ Schedulers.
|
|
|
89
86
|
composed.subscribe(lambda value: print("Received {0}".format(value)))
|
|
90
87
|
|
|
91
88
|
|
|
89
|
+
Fluent and Functional Syntax
|
|
90
|
+
-----------------------------
|
|
91
|
+
|
|
92
|
+
RxPY supports both **fluent** (method chaining) and **functional** (pipe-based) syntax,
|
|
93
|
+
giving you the flexibility to choose the style that works best for your codebase:
|
|
94
|
+
|
|
95
|
+
**Fluent style** - Method chaining for a more Pythonic feel:
|
|
96
|
+
|
|
97
|
+
.. code:: python
|
|
98
|
+
|
|
99
|
+
import reactivex as rx
|
|
100
|
+
|
|
101
|
+
result = (rx.of(1, 2, 3, 4, 5)
|
|
102
|
+
.map(lambda x: x * 2)
|
|
103
|
+
.filter(lambda x: x > 5)
|
|
104
|
+
.reduce(lambda acc, x: acc + x, 0)
|
|
105
|
+
)
|
|
106
|
+
result.subscribe(print) # Output: 24
|
|
107
|
+
|
|
108
|
+
**Functional style** - Pipe-based for functional composition:
|
|
109
|
+
|
|
110
|
+
.. code:: python
|
|
111
|
+
|
|
112
|
+
import reactivex as rx
|
|
113
|
+
from reactivex import operators as ops
|
|
114
|
+
|
|
115
|
+
result = rx.of(1, 2, 3, 4, 5).pipe(
|
|
116
|
+
ops.map(lambda x: x * 2),
|
|
117
|
+
ops.filter(lambda x: x > 5),
|
|
118
|
+
ops.reduce(lambda acc, x: acc + x, 0)
|
|
119
|
+
)
|
|
120
|
+
result.subscribe(print) # Output: 24
|
|
121
|
+
|
|
122
|
+
Both styles are **fully supported** and can even be mixed in the same pipeline.
|
|
123
|
+
Choose the style that best fits your team's preferences and coding standards.
|
|
124
|
+
|
|
125
|
+
|
|
92
126
|
Learning ReactiveX
|
|
93
127
|
------------------
|
|
94
128
|
|
|
@@ -136,8 +170,12 @@ need to be written with an ``_`` in Python:
|
|
|
136
170
|
|
|
137
171
|
.. code:: python
|
|
138
172
|
|
|
173
|
+
# Functional style
|
|
139
174
|
group = source.pipe(ops.group_by(lambda i: i % 3))
|
|
140
175
|
|
|
176
|
+
# Or fluent style
|
|
177
|
+
group = source.group_by(lambda i: i % 3)
|
|
178
|
+
|
|
141
179
|
With ReactiveX for Python you should use `named keyword arguments
|
|
142
180
|
<https://docs.python.org/3/glossary.html>`_ instead of positional arguments when an
|
|
143
181
|
operator has multiple optional arguments. RxPY will not try to detect which arguments
|
|
@@ -146,35 +184,30 @@ you are giving to the operator (or not).
|
|
|
146
184
|
Development
|
|
147
185
|
-----------
|
|
148
186
|
|
|
149
|
-
This project is managed using `
|
|
150
|
-
using `
|
|
151
|
-
<https://github.com/
|
|
152
|
-
<https://github.com/microsoft/pyright>`_ and `mypy <http://mypy-lang.org/>`_.
|
|
187
|
+
This project is managed using `uv <https://docs.astral.sh/uv/>`_. Code is formatted
|
|
188
|
+
using `Ruff <https://github.com/astral-sh/ruff>`_. Code is statically type checked
|
|
189
|
+
using `pyright <https://github.com/microsoft/pyright>`_.
|
|
153
190
|
|
|
154
|
-
|
|
155
|
-
first configure Poetry to make its virtual environment in the
|
|
156
|
-
repository:
|
|
191
|
+
After cloning the repository, install dependencies:
|
|
157
192
|
|
|
158
193
|
.. code:: console
|
|
159
194
|
|
|
160
|
-
|
|
195
|
+
uv sync
|
|
161
196
|
|
|
162
|
-
|
|
197
|
+
Run unit tests:
|
|
163
198
|
|
|
164
199
|
.. code:: console
|
|
165
200
|
|
|
166
|
-
|
|
167
|
-
poetry run pre-commit install
|
|
201
|
+
uv run pytest
|
|
168
202
|
|
|
169
|
-
Run
|
|
203
|
+
Run type checking:
|
|
170
204
|
|
|
171
205
|
.. code:: console
|
|
172
206
|
|
|
173
|
-
|
|
207
|
+
uv run pyright
|
|
174
208
|
|
|
175
209
|
Run code checks (manually):
|
|
176
210
|
|
|
177
211
|
.. code:: console
|
|
178
212
|
|
|
179
|
-
|
|
180
|
-
|
|
213
|
+
uv run pre-commit run --all-files
|