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.
Files changed (219) hide show
  1. reactivex/__init__.py +35 -39
  2. reactivex/_version.py +1 -1
  3. reactivex/abc/disposable.py +3 -4
  4. reactivex/abc/observable.py +13 -6
  5. reactivex/abc/observer.py +2 -1
  6. reactivex/abc/periodicscheduler.py +7 -6
  7. reactivex/abc/scheduler.py +10 -9
  8. reactivex/abc/subject.py +5 -5
  9. reactivex/disposable/compositedisposable.py +4 -4
  10. reactivex/disposable/disposable.py +1 -2
  11. reactivex/disposable/multipleassignmentdisposable.py +2 -3
  12. reactivex/disposable/refcountdisposable.py +1 -2
  13. reactivex/disposable/serialdisposable.py +4 -5
  14. reactivex/disposable/singleassignmentdisposable.py +3 -4
  15. reactivex/internal/__init__.py +2 -0
  16. reactivex/internal/basic.py +2 -2
  17. reactivex/internal/concurrency.py +2 -1
  18. reactivex/internal/curry.py +59 -0
  19. reactivex/internal/exceptions.py +7 -12
  20. reactivex/internal/priorityqueue.py +2 -2
  21. reactivex/internal/utils.py +3 -2
  22. reactivex/notification.py +22 -21
  23. reactivex/observable/case.py +5 -6
  24. reactivex/observable/catch.py +3 -2
  25. reactivex/observable/combinelatest.py +4 -5
  26. reactivex/observable/concat.py +3 -2
  27. reactivex/observable/connectableobservable.py +7 -7
  28. reactivex/observable/defer.py +4 -3
  29. reactivex/observable/empty.py +3 -4
  30. reactivex/observable/forkjoin.py +5 -5
  31. reactivex/observable/fromcallback.py +4 -3
  32. reactivex/observable/fromfuture.py +2 -2
  33. reactivex/observable/fromiterable.py +4 -3
  34. reactivex/observable/generate.py +2 -2
  35. reactivex/observable/generatewithrelativetime.py +4 -3
  36. reactivex/observable/groupedobservable.py +4 -4
  37. reactivex/observable/ifthen.py +3 -2
  38. reactivex/observable/interval.py +1 -4
  39. reactivex/observable/marbles.py +18 -17
  40. reactivex/observable/mixins/__init__.py +32 -0
  41. reactivex/observable/mixins/combination.py +481 -0
  42. reactivex/observable/mixins/conditional.py +135 -0
  43. reactivex/observable/mixins/error_handling.py +130 -0
  44. reactivex/observable/mixins/filtering.py +1119 -0
  45. reactivex/observable/mixins/mathematical.py +277 -0
  46. reactivex/observable/mixins/multicasting.py +306 -0
  47. reactivex/observable/mixins/testing.py +193 -0
  48. reactivex/observable/mixins/time_based.py +209 -0
  49. reactivex/observable/mixins/transformation.py +632 -0
  50. reactivex/observable/mixins/utility.py +811 -0
  51. reactivex/observable/mixins/windowing.py +688 -0
  52. reactivex/observable/never.py +2 -2
  53. reactivex/observable/observable.py +72 -25
  54. reactivex/observable/onerrorresumenext.py +7 -6
  55. reactivex/observable/range.py +6 -6
  56. reactivex/observable/repeat.py +2 -2
  57. reactivex/observable/returnvalue.py +6 -5
  58. reactivex/observable/start.py +3 -2
  59. reactivex/observable/startasync.py +2 -1
  60. reactivex/observable/throw.py +3 -3
  61. reactivex/observable/timer.py +12 -12
  62. reactivex/observable/toasync.py +3 -2
  63. reactivex/observable/using.py +5 -4
  64. reactivex/observable/withlatestfrom.py +4 -5
  65. reactivex/observable/zip.py +7 -6
  66. reactivex/observer/autodetachobserver.py +4 -4
  67. reactivex/observer/observer.py +5 -4
  68. reactivex/observer/scheduledobserver.py +2 -2
  69. reactivex/operators/__init__.py +162 -208
  70. reactivex/operators/_all.py +23 -6
  71. reactivex/operators/_amb.py +88 -75
  72. reactivex/operators/_asobservable.py +20 -17
  73. reactivex/operators/_average.py +48 -45
  74. reactivex/operators/_buffer.py +81 -35
  75. reactivex/operators/_bufferwithtime.py +29 -9
  76. reactivex/operators/_bufferwithtimeorcount.py +27 -8
  77. reactivex/operators/_catch.py +33 -32
  78. reactivex/operators/_combinelatest.py +28 -20
  79. reactivex/operators/_concat.py +16 -13
  80. reactivex/operators/_contains.py +25 -6
  81. reactivex/operators/_count.py +24 -8
  82. reactivex/operators/_debounce.py +141 -138
  83. reactivex/operators/_defaultifempty.py +45 -42
  84. reactivex/operators/_delay.py +24 -23
  85. reactivex/operators/_delaysubscription.py +23 -21
  86. reactivex/operators/_delaywithmapper.py +10 -11
  87. reactivex/operators/_dematerialize.py +25 -21
  88. reactivex/operators/_distinct.py +50 -46
  89. reactivex/operators/_distinctuntilchanged.py +60 -57
  90. reactivex/operators/_do.py +123 -116
  91. reactivex/operators/_dowhile.py +3 -2
  92. reactivex/operators/_elementatordefault.py +57 -33
  93. reactivex/operators/_exclusive.py +59 -53
  94. reactivex/operators/_expand.py +82 -77
  95. reactivex/operators/_filter.py +63 -68
  96. reactivex/operators/_finallyaction.py +3 -2
  97. reactivex/operators/_find.py +49 -32
  98. reactivex/operators/_first.py +18 -11
  99. reactivex/operators/_firstordefault.py +5 -4
  100. reactivex/operators/_flatmap.py +89 -83
  101. reactivex/operators/_forkjoin.py +23 -18
  102. reactivex/operators/_groupby.py +27 -6
  103. reactivex/operators/_groupbyuntil.py +8 -5
  104. reactivex/operators/_groupjoin.py +7 -6
  105. reactivex/operators/_ignoreelements.py +20 -15
  106. reactivex/operators/_isempty.py +15 -4
  107. reactivex/operators/_join.py +6 -5
  108. reactivex/operators/_last.py +36 -31
  109. reactivex/operators/_lastordefault.py +8 -8
  110. reactivex/operators/_map.py +54 -39
  111. reactivex/operators/_materialize.py +30 -31
  112. reactivex/operators/_max.py +18 -11
  113. reactivex/operators/_maxby.py +5 -5
  114. reactivex/operators/_merge.py +132 -129
  115. reactivex/operators/_min.py +16 -10
  116. reactivex/operators/_minby.py +9 -8
  117. reactivex/operators/_multicast.py +9 -9
  118. reactivex/operators/_observeon.py +35 -31
  119. reactivex/operators/_onerrorresumenext.py +2 -1
  120. reactivex/operators/_pairwise.py +38 -34
  121. reactivex/operators/_partition.py +80 -73
  122. reactivex/operators/_pluck.py +4 -3
  123. reactivex/operators/_publish.py +36 -21
  124. reactivex/operators/_publishvalue.py +8 -7
  125. reactivex/operators/_reduce.py +16 -12
  126. reactivex/operators/_repeat.py +33 -30
  127. reactivex/operators/_replay.py +9 -9
  128. reactivex/operators/_retry.py +12 -10
  129. reactivex/operators/_sample.py +31 -27
  130. reactivex/operators/_scan.py +41 -39
  131. reactivex/operators/_sequenceequal.py +8 -7
  132. reactivex/operators/_single.py +20 -13
  133. reactivex/operators/_singleordefault.py +6 -5
  134. reactivex/operators/_skip.py +35 -32
  135. reactivex/operators/_skiplast.py +38 -34
  136. reactivex/operators/_skiplastwithtime.py +5 -4
  137. reactivex/operators/_skipuntil.py +40 -35
  138. reactivex/operators/_skipuntilwithtime.py +4 -3
  139. reactivex/operators/_skipwhile.py +65 -44
  140. reactivex/operators/_skipwithtime.py +50 -46
  141. reactivex/operators/_slice.py +58 -53
  142. reactivex/operators/_some.py +48 -47
  143. reactivex/operators/_startswith.py +17 -15
  144. reactivex/operators/_subscribeon.py +44 -41
  145. reactivex/operators/_sum.py +23 -6
  146. reactivex/operators/_switchlatest.py +71 -69
  147. reactivex/operators/_take.py +37 -33
  148. reactivex/operators/_takelast.py +37 -36
  149. reactivex/operators/_takelastbuffer.py +38 -37
  150. reactivex/operators/_takelastwithtime.py +60 -56
  151. reactivex/operators/_takeuntil.py +33 -32
  152. reactivex/operators/_takeuntilwithtime.py +42 -39
  153. reactivex/operators/_takewhile.py +108 -100
  154. reactivex/operators/_takewithtime.py +46 -41
  155. reactivex/operators/_throttlefirst.py +52 -45
  156. reactivex/operators/_timeinterval.py +40 -36
  157. reactivex/operators/_timeout.py +81 -79
  158. reactivex/operators/_timeoutwithmapper.py +6 -5
  159. reactivex/operators/_timestamp.py +24 -22
  160. reactivex/operators/_todict.py +51 -43
  161. reactivex/operators/_tofuture.py +24 -15
  162. reactivex/operators/_toiterable.py +33 -27
  163. reactivex/operators/_tomarbles.py +5 -5
  164. reactivex/operators/_toset.py +29 -19
  165. reactivex/operators/_whiledo.py +2 -1
  166. reactivex/operators/_window.py +100 -99
  167. reactivex/operators/_windowwithcount.py +56 -54
  168. reactivex/operators/_windowwithtime.py +95 -79
  169. reactivex/operators/_windowwithtimeorcount.py +85 -69
  170. reactivex/operators/_withlatestfrom.py +13 -9
  171. reactivex/operators/_zip.py +67 -63
  172. reactivex/operators/connectable/_refcount.py +4 -3
  173. reactivex/pipe.py +2 -1
  174. reactivex/run.py +8 -4
  175. reactivex/scheduler/catchscheduler.py +11 -10
  176. reactivex/scheduler/currentthreadscheduler.py +2 -3
  177. reactivex/scheduler/eventloop/asyncioscheduler.py +7 -6
  178. reactivex/scheduler/eventloop/asynciothreadsafescheduler.py +12 -14
  179. reactivex/scheduler/eventloop/eventletscheduler.py +4 -4
  180. reactivex/scheduler/eventloop/geventscheduler.py +4 -4
  181. reactivex/scheduler/eventloop/ioloopscheduler.py +4 -4
  182. reactivex/scheduler/eventloop/twistedscheduler.py +4 -4
  183. reactivex/scheduler/eventloopscheduler.py +9 -12
  184. reactivex/scheduler/historicalscheduler.py +1 -2
  185. reactivex/scheduler/immediatescheduler.py +5 -4
  186. reactivex/scheduler/mainloop/gtkscheduler.py +6 -7
  187. reactivex/scheduler/mainloop/pygamescheduler.py +4 -4
  188. reactivex/scheduler/mainloop/qtscheduler.py +6 -6
  189. reactivex/scheduler/mainloop/tkinterscheduler.py +4 -4
  190. reactivex/scheduler/mainloop/wxscheduler.py +7 -7
  191. reactivex/scheduler/newthreadscheduler.py +6 -8
  192. reactivex/scheduler/periodicscheduler.py +4 -4
  193. reactivex/scheduler/scheduleditem.py +4 -4
  194. reactivex/scheduler/scheduler.py +5 -5
  195. reactivex/scheduler/threadpoolscheduler.py +3 -3
  196. reactivex/scheduler/timeoutscheduler.py +5 -4
  197. reactivex/scheduler/trampoline.py +1 -2
  198. reactivex/scheduler/trampolinescheduler.py +5 -6
  199. reactivex/scheduler/virtualtimescheduler.py +4 -4
  200. reactivex/subject/asyncsubject.py +2 -2
  201. reactivex/subject/behaviorsubject.py +2 -2
  202. reactivex/subject/innersubscription.py +2 -2
  203. reactivex/subject/replaysubject.py +8 -8
  204. reactivex/subject/subject.py +4 -4
  205. reactivex/testing/coldobservable.py +5 -5
  206. reactivex/testing/hotobservable.py +6 -6
  207. reactivex/testing/marbles.py +21 -20
  208. reactivex/testing/mockdisposable.py +1 -3
  209. reactivex/testing/mockobserver.py +2 -2
  210. reactivex/testing/reactivetest.py +2 -2
  211. reactivex/testing/recorded.py +1 -1
  212. reactivex/testing/subscription.py +3 -3
  213. reactivex/testing/testscheduler.py +13 -12
  214. reactivex/typing.py +25 -14
  215. {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/METADATA +59 -26
  216. reactivex-5.0.0a2.dist-info/RECORD +236 -0
  217. {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/WHEEL +1 -1
  218. reactivex-4.1.0.dist-info/RECORD +0 -223
  219. {reactivex-4.1.0.dist-info → reactivex-5.0.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Optional, TypeVar, cast
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: Optional[abc.SchedulerBase] = None,
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 Optional, TypeVar, cast
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: Optional[abc.SchedulerBase] = None,
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, Optional, TypeVar
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: Optional[abc.ObserverBase[_T]] = None
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, Deque, NamedTuple, Optional, TypeVar, cast
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: Optional[int] = None,
41
- window: Optional[typing.RelativeTime] = None,
42
- scheduler: Optional[abc.SchedulerBase] = None,
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.window = (
57
+ self._window = (
58
58
  timedelta.max if window is None else self.scheduler.to_timedelta(window)
59
59
  )
60
- self.queue: Deque[QueueItem] = deque()
60
+ self.queue: deque[QueueItem] = deque()
61
61
 
62
62
  def _subscribe_core(
63
63
  self,
64
64
  observer: abc.ObserverBase[_T],
65
- scheduler: Optional[abc.SchedulerBase] = None,
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.window:
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:
@@ -1,5 +1,5 @@
1
1
  import threading
2
- from typing import List, Optional, TypeVar
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: List[abc.ObserverBase[_T]] = []
25
- self.exception: Optional[Exception] = None
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: Optional[abc.SchedulerBase] = None,
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, List, Optional, TypeVar
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: List[Recorded[_T]]
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: List[Subscription] = []
21
+ self.subscriptions: list[Subscription] = []
22
22
 
23
23
  def _subscribe_core(
24
24
  self,
25
- observer: Optional[abc.ObserverBase[_T]] = None,
26
- scheduler: Optional[abc.SchedulerBase] = None,
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, List, Optional, TypeVar
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: List[Recorded[_T]]
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: List[Subscription] = []
23
- self.observers: List[abc.ObserverBase[_T]] = []
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: Optional[abc.ObserverBase[_T]] = None,
47
- scheduler: Optional[abc.SchedulerBase] = None,
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)
@@ -1,5 +1,6 @@
1
+ from collections.abc import Generator
1
2
  from contextlib import contextmanager
2
- from typing import Any, Dict, Generator, List, NamedTuple, Optional, Tuple, Union
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
- [Union[Observable[Any], Callable[[], Observable[Any]]]], List[Recorded[Any]]
22
+ [Observable[Any] | Callable[[], Observable[Any]]], list[Recorded[Any]]
22
23
  ]
23
24
  cold: Callable[
24
- [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]],
25
+ [str, dict[str | float, Any] | None, Exception | None],
25
26
  Observable[Any],
26
27
  ]
27
28
  hot: Callable[
28
- [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]],
29
+ [str, dict[str | float, Any] | None, Exception | None],
29
30
  Observable[Any],
30
31
  ]
31
32
  exp: Callable[
32
- [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]],
33
- List[Recorded[Any]],
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: Union[Observable[Any], Callable[[], Observable[Any]]],
101
- ) -> List[Recorded[Any]]:
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: Optional[Dict[Union[str, float], Any]] = None,
127
- error: Optional[Exception] = None,
128
- ) -> List[Recorded[Any]]:
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: Optional[Dict[Union[str, float], Any]] = None,
141
- error: Optional[Exception] = None,
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: Optional[Dict[Union[str, float], Any]] = None,
154
- error: Optional[Exception] = None,
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: List[Tuple[typing.RelativeTime, Notification[Any]]],
175
- ) -> List[Recorded[Any]]:
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: List[Recorded[Any]] = []
181
+ records: list[Recorded[Any]] = []
181
182
 
182
183
  for message in messages:
183
184
  time, notification = message
184
- if isinstance(time, (int, float)):
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: List[typing.AbsoluteTime] = []
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 List, TypeVar
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: List[Recorded[_T]] = []
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, Union
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: Union[Exception, str, types.FunctionType]
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))
@@ -38,4 +38,4 @@ class Recorded(Generic[_T]):
38
38
  return str(self)
39
39
 
40
40
  def __str__(self) -> str:
41
- return "%s@%s" % (self.value, self.time)
41
+ return f"{self.value}@{self.time}"
@@ -1,9 +1,9 @@
1
1
  import sys
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
 
4
4
 
5
5
  class Subscription:
6
- def __init__(self, start: int, end: Optional[int] = None):
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 "(%s, %s)" % (self.subscribe, unsubscribe)
25
+ return f"({self.subscribe}, {unsubscribe})"
@@ -1,4 +1,5 @@
1
- from typing import Any, Callable, List, Optional, TypeVar, Union, cast
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: Optional[Callable[[], Observable[_T]]] = None,
50
- created: Optional[float] = None,
51
- subscribed: Optional[float] = None,
52
- disposed: Optional[float] = None,
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: Optional[abc.DisposableBase] = None
80
- source: Optional[abc.ObservableBase[_T]] = None
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: Union[Recorded[_T], List[Recorded[_T]]]
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], List):
131
+ if args and isinstance(args[0], list):
131
132
  messages = args[0]
132
133
  else:
133
- messages = cast(List[Recorded[_T]], list(args))
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: Union[Recorded[_T], List[Recorded[_T]]]
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(List[Recorded[_T]], list(args))
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 Callable, TypeVar, Union
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
- Action = Callable[[], None]
23
-
24
- Mapper = Callable[[_T1], _T2]
25
- MapperIndexed = Callable[[_T1, int], _T2]
26
- Predicate = Callable[[_T1], bool]
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
- Startable = Union[StartableBase, Thread]
34
- StartableTarget = Callable[..., None]
35
- StartableFactory = Callable[[StartableTarget], Startable]
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: 4.1.0
3
+ Version: 5.0.0a2
4
4
  Summary: ReactiveX (Rx) for Python
5
- License: MIT
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-Dist: typing-extensions (>=4.1.1,<5.0.0)
26
- Project-URL: Documentation, https://rxpy.readthedocs.io/en/latest/
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 `Poetry <https://python-poetry.org/>`_. Code is formatted
150
- using `Black <https://github.com/psf/black>`_, `isort
151
- <https://github.com/PyCQA/isort>`_. Code is statically type checked using `pyright
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
- If you want to take advantage of the default VSCode integration, then
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
- poetry config virtualenvs.in-project true
195
+ uv sync
161
196
 
162
- After cloning the repository, activate the tooling:
197
+ Run unit tests:
163
198
 
164
199
  .. code:: console
165
200
 
166
- poetry install
167
- poetry run pre-commit install
201
+ uv run pytest
168
202
 
169
- Run unit tests:
203
+ Run type checking:
170
204
 
171
205
  .. code:: console
172
206
 
173
- poetry run pytest
207
+ uv run pyright
174
208
 
175
209
  Run code checks (manually):
176
210
 
177
211
  .. code:: console
178
212
 
179
- poetry run pre-commit run --all-files
180
-
213
+ uv run pre-commit run --all-files