venv added, updated
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from .connectableobservable import ConnectableObservable
|
||||
from .groupedobservable import GroupedObservable
|
||||
from .observable import Observable
|
||||
|
||||
__all__ = ["Observable", "ConnectableObservable", "GroupedObservable"]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,31 @@
|
||||
from typing import TypeVar
|
||||
|
||||
from reactivex import Observable, never
|
||||
from reactivex import operators as _
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def amb_(*sources: Observable[_T]) -> Observable[_T]:
|
||||
"""Propagates the observable sequence that reacts first.
|
||||
|
||||
Example:
|
||||
>>> winner = amb(xs, ys, zs)
|
||||
|
||||
Returns:
|
||||
An observable sequence that surfaces any of the given sequences,
|
||||
whichever reacted first.
|
||||
"""
|
||||
|
||||
acc: Observable[_T] = never()
|
||||
|
||||
def func(previous: Observable[_T], current: Observable[_T]) -> Observable[_T]:
|
||||
return _.amb(previous)(current)
|
||||
|
||||
for source in sources:
|
||||
acc = func(acc, source)
|
||||
|
||||
return acc
|
||||
|
||||
|
||||
__all__ = ["amb_"]
|
||||
@@ -0,0 +1,35 @@
|
||||
from asyncio import Future
|
||||
from typing import Callable, Mapping, Optional, TypeVar, Union
|
||||
|
||||
from reactivex import Observable, abc, defer, empty, from_future
|
||||
|
||||
_Key = TypeVar("_Key")
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def case_(
|
||||
mapper: Callable[[], _Key],
|
||||
sources: Mapping[_Key, Observable[_T]],
|
||||
default_source: Optional[Union[Observable[_T], "Future[_T]"]] = None,
|
||||
) -> Observable[_T]:
|
||||
|
||||
default_source_: Union[Observable[_T], "Future[_T]"] = default_source or empty()
|
||||
|
||||
def factory(_: abc.SchedulerBase) -> Observable[_T]:
|
||||
try:
|
||||
result: Union[Observable[_T], "Future[_T]"] = sources[mapper()]
|
||||
except KeyError:
|
||||
result = default_source_
|
||||
|
||||
if isinstance(result, Future):
|
||||
|
||||
result_: Observable[_T] = from_future(result)
|
||||
else:
|
||||
result_ = result
|
||||
|
||||
return result_
|
||||
|
||||
return defer(factory)
|
||||
|
||||
|
||||
__all__ = ["case_"]
|
||||
@@ -0,0 +1,84 @@
|
||||
from typing import Any, Iterable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import (
|
||||
CompositeDisposable,
|
||||
Disposable,
|
||||
SerialDisposable,
|
||||
SingleAssignmentDisposable,
|
||||
)
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def catch_with_iterable_(sources: Iterable[Observable[_T]]) -> Observable[_T]:
|
||||
|
||||
"""Continues an observable sequence that is terminated by an
|
||||
exception with the next observable sequence.
|
||||
|
||||
Examples:
|
||||
>>> res = catch([xs, ys, zs])
|
||||
>>> res = reactivex.catch(src for src in [xs, ys, zs])
|
||||
|
||||
Args:
|
||||
sources: an Iterable of observables. Thus a generator is accepted.
|
||||
|
||||
Returns:
|
||||
An observable sequence containing elements from consecutive
|
||||
source sequences until a source sequence terminates
|
||||
successfully.
|
||||
"""
|
||||
|
||||
sources_ = iter(sources)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler_ or CurrentThreadScheduler.singleton()
|
||||
|
||||
subscription = SerialDisposable()
|
||||
cancelable = SerialDisposable()
|
||||
last_exception = None
|
||||
is_disposed = False
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
def on_error(exn: Exception) -> None:
|
||||
nonlocal last_exception
|
||||
last_exception = exn
|
||||
cancelable.disposable = _scheduler.schedule(action)
|
||||
|
||||
if is_disposed:
|
||||
return
|
||||
|
||||
try:
|
||||
current = next(sources_)
|
||||
except StopIteration:
|
||||
if last_exception:
|
||||
observer.on_error(last_exception)
|
||||
else:
|
||||
observer.on_completed()
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
observer.on_error(ex)
|
||||
else:
|
||||
d = SingleAssignmentDisposable()
|
||||
subscription.disposable = d
|
||||
d.disposable = current.subscribe(
|
||||
observer.on_next,
|
||||
on_error,
|
||||
observer.on_completed,
|
||||
scheduler=scheduler_,
|
||||
)
|
||||
|
||||
cancelable.disposable = _scheduler.schedule(action)
|
||||
|
||||
def dispose() -> None:
|
||||
nonlocal is_disposed
|
||||
is_disposed = True
|
||||
|
||||
return CompositeDisposable(subscription, cancelable, Disposable(dispose))
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["catch_with_iterable_"]
|
||||
@@ -0,0 +1,76 @@
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import CompositeDisposable, SingleAssignmentDisposable
|
||||
|
||||
|
||||
def combine_latest_(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]:
|
||||
"""Merges the specified observable sequences into one observable
|
||||
sequence by creating a tuple whenever any of the
|
||||
observable sequences produces an element.
|
||||
|
||||
Examples:
|
||||
>>> obs = combine_latest(obs1, obs2, obs3)
|
||||
|
||||
Returns:
|
||||
An observable sequence containing the result of combining
|
||||
elements of the sources into a tuple.
|
||||
"""
|
||||
|
||||
parent = sources[0]
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> CompositeDisposable:
|
||||
|
||||
n = len(sources)
|
||||
has_value = [False] * n
|
||||
has_value_all = [False]
|
||||
is_done = [False] * n
|
||||
values = [None] * n
|
||||
|
||||
def _next(i: Any) -> None:
|
||||
has_value[i] = True
|
||||
|
||||
if has_value_all[0] or all(has_value):
|
||||
res = tuple(values)
|
||||
observer.on_next(res)
|
||||
|
||||
elif all([x for j, x in enumerate(is_done) if j != i]):
|
||||
observer.on_completed()
|
||||
|
||||
has_value_all[0] = all(has_value)
|
||||
|
||||
def done(i: Any) -> None:
|
||||
is_done[i] = True
|
||||
if all(is_done):
|
||||
observer.on_completed()
|
||||
|
||||
subscriptions: List[Optional[SingleAssignmentDisposable]] = [None] * n
|
||||
|
||||
def func(i: int) -> None:
|
||||
subscriptions[i] = SingleAssignmentDisposable()
|
||||
|
||||
def on_next(x: Any) -> None:
|
||||
with parent.lock:
|
||||
values[i] = x
|
||||
_next(i)
|
||||
|
||||
def on_completed() -> None:
|
||||
with parent.lock:
|
||||
done(i)
|
||||
|
||||
subscription = subscriptions[i]
|
||||
assert subscription
|
||||
subscription.disposable = sources[i].subscribe(
|
||||
on_next, observer.on_error, on_completed, scheduler=scheduler
|
||||
)
|
||||
|
||||
for idx in range(n):
|
||||
func(idx)
|
||||
return CompositeDisposable(subscriptions)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["combine_latest_"]
|
||||
@@ -0,0 +1,62 @@
|
||||
from typing import Any, Iterable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import (
|
||||
CompositeDisposable,
|
||||
Disposable,
|
||||
SerialDisposable,
|
||||
SingleAssignmentDisposable,
|
||||
)
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def concat_with_iterable_(sources: Iterable[Observable[_T]]) -> Observable[_T]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler_ or CurrentThreadScheduler.singleton()
|
||||
|
||||
sources_ = iter(sources)
|
||||
|
||||
subscription = SerialDisposable()
|
||||
cancelable = SerialDisposable()
|
||||
is_disposed = False
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
nonlocal is_disposed
|
||||
if is_disposed:
|
||||
return
|
||||
|
||||
def on_completed() -> None:
|
||||
cancelable.disposable = _scheduler.schedule(action)
|
||||
|
||||
try:
|
||||
current = next(sources_)
|
||||
except StopIteration:
|
||||
observer.on_completed()
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
observer.on_error(ex)
|
||||
else:
|
||||
d = SingleAssignmentDisposable()
|
||||
subscription.disposable = d
|
||||
d.disposable = current.subscribe(
|
||||
observer.on_next,
|
||||
observer.on_error,
|
||||
on_completed,
|
||||
scheduler=scheduler_,
|
||||
)
|
||||
|
||||
cancelable.disposable = _scheduler.schedule(action)
|
||||
|
||||
def dispose() -> None:
|
||||
nonlocal is_disposed
|
||||
is_disposed = True
|
||||
|
||||
return CompositeDisposable(subscription, cancelable, Disposable(dispose))
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["concat_with_iterable_"]
|
||||
@@ -0,0 +1,82 @@
|
||||
from typing import List, Optional, TypeVar
|
||||
|
||||
from reactivex import abc
|
||||
from reactivex.disposable import CompositeDisposable, Disposable
|
||||
|
||||
from .observable import Observable
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class ConnectableObservable(Observable[_T]):
|
||||
"""Represents an observable that can be connected and
|
||||
disconnected."""
|
||||
|
||||
def __init__(self, source: abc.ObservableBase[_T], subject: abc.SubjectBase[_T]):
|
||||
self.subject = subject
|
||||
self.has_subscription = False
|
||||
self.subscription: Optional[abc.DisposableBase] = None
|
||||
self.source = source
|
||||
|
||||
super().__init__()
|
||||
|
||||
def _subscribe_core(
|
||||
self,
|
||||
observer: abc.ObserverBase[_T],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
return self.subject.subscribe(observer, scheduler=scheduler)
|
||||
|
||||
def connect(
|
||||
self, scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Optional[abc.DisposableBase]:
|
||||
"""Connects the observable."""
|
||||
|
||||
if not self.has_subscription:
|
||||
self.has_subscription = True
|
||||
|
||||
def dispose() -> None:
|
||||
self.has_subscription = False
|
||||
|
||||
subscription = self.source.subscribe(self.subject, scheduler=scheduler)
|
||||
self.subscription = CompositeDisposable(subscription, Disposable(dispose))
|
||||
|
||||
return self.subscription
|
||||
|
||||
def auto_connect(self, subscriber_count: int = 1) -> Observable[_T]:
|
||||
"""Returns an observable sequence that stays connected to the
|
||||
source indefinitely to the observable sequence.
|
||||
Providing a subscriber_count will cause it to connect() after
|
||||
that many subscriptions occur. A subscriber_count of 0 will
|
||||
result in emissions firing immediately without waiting for
|
||||
subscribers.
|
||||
"""
|
||||
|
||||
connectable_subscription: List[Optional[abc.DisposableBase]] = [None]
|
||||
count = [0]
|
||||
source = self
|
||||
is_connected = [False]
|
||||
|
||||
if subscriber_count == 0:
|
||||
connectable_subscription[0] = source.connect()
|
||||
is_connected[0] = True
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
count[0] += 1
|
||||
should_connect = count[0] == subscriber_count and not is_connected[0]
|
||||
subscription = source.subscribe(observer)
|
||||
if should_connect:
|
||||
connectable_subscription[0] = source.connect(scheduler)
|
||||
is_connected[0] = True
|
||||
|
||||
def dispose() -> None:
|
||||
subscription.dispose()
|
||||
count[0] -= 1
|
||||
is_connected[0] = False
|
||||
|
||||
return Disposable(dispose)
|
||||
|
||||
return Observable(subscribe)
|
||||
@@ -0,0 +1,43 @@
|
||||
from asyncio import Future
|
||||
from typing import Callable, Optional, TypeVar, Union
|
||||
|
||||
from reactivex import Observable, abc, from_future, throw
|
||||
from reactivex.scheduler import ImmediateScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def defer_(
|
||||
factory: Callable[[abc.SchedulerBase], Union[Observable[_T], "Future[_T]"]]
|
||||
) -> Observable[_T]:
|
||||
"""Returns an observable sequence that invokes the specified factory
|
||||
function whenever a new observer subscribes.
|
||||
|
||||
Example:
|
||||
>>> res = defer(lambda scheduler: of(1, 2, 3))
|
||||
|
||||
Args:
|
||||
observable_factory: Observable factory function to invoke for
|
||||
each observer that subscribes to the resulting sequence. The
|
||||
factory takes a single argument, the scheduler used.
|
||||
|
||||
Returns:
|
||||
An observable sequence whose observers trigger an invocation
|
||||
of the given observable factory function.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
try:
|
||||
result = factory(scheduler or ImmediateScheduler.singleton())
|
||||
except Exception as ex: # By design. pylint: disable=W0703
|
||||
return throw(ex).subscribe(observer)
|
||||
|
||||
result = from_future(result) if isinstance(result, Future) else result
|
||||
return result.subscribe(observer, scheduler=scheduler)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["defer_"]
|
||||
@@ -0,0 +1,22 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.scheduler import ImmediateScheduler
|
||||
|
||||
|
||||
def empty_(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
|
||||
_scheduler = scheduler or scheduler_ or ImmediateScheduler.singleton()
|
||||
|
||||
def action(_: abc.SchedulerBase, __: Any) -> None:
|
||||
observer.on_completed()
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["empty_"]
|
||||
@@ -0,0 +1,72 @@
|
||||
from typing import Any, List, Optional, Tuple, cast
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import CompositeDisposable, SingleAssignmentDisposable
|
||||
|
||||
|
||||
def fork_join_(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]:
|
||||
"""Wait for observables to complete and then combine last values
|
||||
they emitted into a tuple. Whenever any of that observables completes
|
||||
without emitting any value, result sequence will complete at that moment as well.
|
||||
|
||||
Examples:
|
||||
>>> obs = reactivex.fork_join(obs1, obs2, obs3)
|
||||
|
||||
Returns:
|
||||
An observable sequence containing the result of combining last element from
|
||||
each source in given sequence.
|
||||
"""
|
||||
|
||||
parent = sources[0]
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Tuple[Any, ...]],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
n = len(sources)
|
||||
values = [None] * n
|
||||
is_done = [False] * n
|
||||
has_value = [False] * n
|
||||
|
||||
def done(i: int) -> None:
|
||||
is_done[i] = True
|
||||
|
||||
if not has_value[i]:
|
||||
observer.on_completed()
|
||||
return
|
||||
|
||||
if all(is_done):
|
||||
if all(has_value):
|
||||
observer.on_next(tuple(values))
|
||||
observer.on_completed()
|
||||
else:
|
||||
observer.on_completed()
|
||||
|
||||
subscriptions: List[SingleAssignmentDisposable] = [
|
||||
cast(SingleAssignmentDisposable, None)
|
||||
] * n
|
||||
|
||||
def _subscribe(i: int) -> None:
|
||||
subscriptions[i] = SingleAssignmentDisposable()
|
||||
|
||||
def on_next(value: Any) -> None:
|
||||
with parent.lock:
|
||||
values[i] = value
|
||||
has_value[i] = True
|
||||
|
||||
def on_completed() -> None:
|
||||
with parent.lock:
|
||||
done(i)
|
||||
|
||||
subscriptions[i].disposable = sources[i].subscribe(
|
||||
on_next, observer.on_error, on_completed, scheduler=scheduler
|
||||
)
|
||||
|
||||
for i in range(n):
|
||||
_subscribe(i)
|
||||
return CompositeDisposable(subscriptions)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["fork_join_"]
|
||||
@@ -0,0 +1,59 @@
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from reactivex import Observable, abc, typing
|
||||
from reactivex.disposable import Disposable
|
||||
|
||||
|
||||
def from_callback_(
|
||||
func: Callable[..., Callable[..., None]],
|
||||
mapper: Optional[typing.Mapper[Any, Any]] = None,
|
||||
) -> Callable[[], Observable[Any]]:
|
||||
"""Converts a callback function to an observable sequence.
|
||||
|
||||
Args:
|
||||
func: Function with a callback as the last argument to
|
||||
convert to an Observable sequence.
|
||||
mapper: [Optional] A mapper which takes the arguments
|
||||
from the callback to produce a single item to yield on next.
|
||||
|
||||
Returns:
|
||||
A function, when executed with the required arguments minus
|
||||
the callback, produces an Observable sequence with a single value of
|
||||
the arguments to the callback as a list.
|
||||
"""
|
||||
|
||||
def function(*args: Any) -> Observable[Any]:
|
||||
arguments = list(args)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
def handler(*args: Any) -> None:
|
||||
results = list(args)
|
||||
if mapper:
|
||||
try:
|
||||
results = mapper(args)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
observer.on_error(err)
|
||||
return
|
||||
|
||||
observer.on_next(results)
|
||||
else:
|
||||
if len(results) <= 1:
|
||||
observer.on_next(*results)
|
||||
else:
|
||||
observer.on_next(results)
|
||||
|
||||
observer.on_completed()
|
||||
|
||||
arguments.append(handler)
|
||||
func(*arguments)
|
||||
return Disposable()
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
return function
|
||||
|
||||
|
||||
__all__ = ["from_callback_"]
|
||||
@@ -0,0 +1,49 @@
|
||||
import asyncio
|
||||
from asyncio import Future
|
||||
from typing import Any, Optional, TypeVar, cast
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import Disposable
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def from_future_(future: "Future[_T]") -> Observable[_T]:
|
||||
"""Converts a Future to an Observable sequence
|
||||
|
||||
Args:
|
||||
future -- A Python 3 compatible future.
|
||||
https://docs.python.org/3/library/asyncio-task.html#future
|
||||
|
||||
Returns:
|
||||
An Observable sequence which wraps the existing future success
|
||||
and failure.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
def done(future: "Future[_T]") -> None:
|
||||
try:
|
||||
value: Any = future.result()
|
||||
except Exception as ex:
|
||||
observer.on_error(ex)
|
||||
except asyncio.CancelledError as ex: # pylint: disable=broad-except
|
||||
# asyncio.CancelledError is a BaseException, so need to cast
|
||||
observer.on_error(cast(Exception, ex))
|
||||
else:
|
||||
observer.on_next(value)
|
||||
observer.on_completed()
|
||||
|
||||
future.add_done_callback(done)
|
||||
|
||||
def dispose() -> None:
|
||||
if future:
|
||||
future.cancel()
|
||||
|
||||
return Disposable(dispose)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["from_future_"]
|
||||
@@ -0,0 +1,56 @@
|
||||
from typing import Any, Iterable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import CompositeDisposable, Disposable
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def from_iterable_(
|
||||
iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[_T]:
|
||||
"""Converts an iterable to an observable sequence.
|
||||
|
||||
Example:
|
||||
>>> from_iterable([1,2,3])
|
||||
|
||||
Args:
|
||||
iterable: A Python iterable
|
||||
scheduler: An optional scheduler to schedule the values on.
|
||||
|
||||
Returns:
|
||||
The observable sequence whose elements are pulled from the
|
||||
given iterable sequence.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton()
|
||||
iterator = iter(iterable)
|
||||
disposed = False
|
||||
|
||||
def action(_: abc.SchedulerBase, __: Any = None) -> None:
|
||||
nonlocal disposed
|
||||
|
||||
try:
|
||||
while not disposed:
|
||||
value = next(iterator)
|
||||
observer.on_next(value)
|
||||
except StopIteration:
|
||||
observer.on_completed()
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
observer.on_error(error)
|
||||
|
||||
def dispose() -> None:
|
||||
nonlocal disposed
|
||||
disposed = True
|
||||
|
||||
disp = Disposable(dispose)
|
||||
return CompositeDisposable(_scheduler.schedule(action), disp)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["from_iterable_"]
|
||||
@@ -0,0 +1,57 @@
|
||||
from typing import Any, Optional, TypeVar, cast
|
||||
|
||||
from reactivex import Observable, abc, typing
|
||||
from reactivex.disposable import MultipleAssignmentDisposable
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_TState = TypeVar("_TState")
|
||||
|
||||
|
||||
def generate_(
|
||||
initial_state: _TState,
|
||||
condition: typing.Predicate[_TState],
|
||||
iterate: typing.Mapper[_TState, _TState],
|
||||
) -> Observable[_TState]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_TState],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
scheduler = scheduler or CurrentThreadScheduler.singleton()
|
||||
first = True
|
||||
state = initial_state
|
||||
mad = MultipleAssignmentDisposable()
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state1: Any = None) -> None:
|
||||
nonlocal first
|
||||
nonlocal state
|
||||
|
||||
has_result = False
|
||||
result: _TState = cast(_TState, None)
|
||||
|
||||
try:
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
state = iterate(state)
|
||||
|
||||
has_result = condition(state)
|
||||
if has_result:
|
||||
result = state
|
||||
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
observer.on_error(exception)
|
||||
return
|
||||
|
||||
if has_result:
|
||||
observer.on_next(result)
|
||||
mad.disposable = scheduler.schedule(action)
|
||||
else:
|
||||
observer.on_completed()
|
||||
|
||||
mad.disposable = scheduler.schedule(action)
|
||||
return mad
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["generate_"]
|
||||
@@ -0,0 +1,89 @@
|
||||
from typing import Any, Callable, Optional, TypeVar, cast
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import MultipleAssignmentDisposable
|
||||
from reactivex.scheduler import TimeoutScheduler
|
||||
from reactivex.typing import Mapper, Predicate, RelativeTime
|
||||
|
||||
_TState = TypeVar("_TState")
|
||||
|
||||
|
||||
def generate_with_relative_time_(
|
||||
initial_state: _TState,
|
||||
condition: Predicate[_TState],
|
||||
iterate: Mapper[_TState, _TState],
|
||||
time_mapper: Callable[[_TState], RelativeTime],
|
||||
) -> Observable[_TState]:
|
||||
"""Generates an observable sequence by iterating a state from an
|
||||
initial state until the condition fails.
|
||||
|
||||
Example:
|
||||
res = source.generate_with_relative_time(
|
||||
0, lambda x: True, lambda x: x + 1, lambda x: 0.5
|
||||
)
|
||||
|
||||
Args:
|
||||
initial_state: Initial state.
|
||||
condition: Condition to terminate generation (upon returning
|
||||
false).
|
||||
iterate: Iteration step function.
|
||||
time_mapper: Time mapper function to control the speed of
|
||||
values being produced each iteration, returning relative
|
||||
times, i.e. either floats denoting seconds or instances of
|
||||
timedelta.
|
||||
|
||||
Returns:
|
||||
The generated sequence.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_TState],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
scheduler = scheduler or TimeoutScheduler.singleton()
|
||||
mad = MultipleAssignmentDisposable()
|
||||
state = initial_state
|
||||
has_result = False
|
||||
result: _TState = cast(_TState, None)
|
||||
first = True
|
||||
time: Optional[RelativeTime] = None
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, _: Any) -> None:
|
||||
nonlocal state
|
||||
nonlocal has_result
|
||||
nonlocal result
|
||||
nonlocal first
|
||||
nonlocal time
|
||||
|
||||
if has_result:
|
||||
observer.on_next(result)
|
||||
|
||||
try:
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
state = iterate(state)
|
||||
|
||||
has_result = condition(state)
|
||||
|
||||
if has_result:
|
||||
result = state
|
||||
time = time_mapper(state)
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
observer.on_error(e)
|
||||
return
|
||||
|
||||
if has_result:
|
||||
assert time
|
||||
mad.disposable = scheduler.schedule_relative(time, action)
|
||||
else:
|
||||
observer.on_completed()
|
||||
|
||||
mad.disposable = scheduler.schedule_relative(0, action)
|
||||
return mad
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["generate_with_relative_time_"]
|
||||
@@ -0,0 +1,40 @@
|
||||
from typing import Generic, Optional, TypeVar
|
||||
|
||||
from reactivex import abc
|
||||
from reactivex.disposable import CompositeDisposable, Disposable, RefCountDisposable
|
||||
|
||||
from .observable import Observable
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_TKey = TypeVar("_TKey")
|
||||
|
||||
|
||||
class GroupedObservable(Generic[_TKey, _T], Observable[_T]):
|
||||
def __init__(
|
||||
self,
|
||||
key: _TKey,
|
||||
underlying_observable: Observable[_T],
|
||||
merged_disposable: Optional[RefCountDisposable] = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.key = key
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
return CompositeDisposable(
|
||||
merged_disposable.disposable if merged_disposable else Disposable(),
|
||||
underlying_observable.subscribe(observer, scheduler=scheduler),
|
||||
)
|
||||
|
||||
self.underlying_observable = (
|
||||
underlying_observable if not merged_disposable else Observable(subscribe)
|
||||
)
|
||||
|
||||
def _subscribe_core(
|
||||
self,
|
||||
observer: abc.ObserverBase[_T],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
return self.underlying_observable.subscribe(observer, scheduler=scheduler)
|
||||
@@ -0,0 +1,55 @@
|
||||
from asyncio import Future
|
||||
from typing import Callable, TypeVar, Union
|
||||
|
||||
import reactivex
|
||||
from reactivex import Observable, abc
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def if_then_(
|
||||
condition: Callable[[], bool],
|
||||
then_source: Union[Observable[_T], "Future[_T]"],
|
||||
else_source: Union[None, Observable[_T], "Future[_T]"] = None,
|
||||
) -> Observable[_T]:
|
||||
"""Determines whether an observable collection contains values.
|
||||
|
||||
Example:
|
||||
1 - res = reactivex.if_then(condition, obs1)
|
||||
2 - res = reactivex.if_then(condition, obs1, obs2)
|
||||
|
||||
Args:
|
||||
condition: The condition which determines if the then_source or
|
||||
else_source will be run.
|
||||
then_source: The observable sequence or Promise that
|
||||
will be run if the condition function returns true.
|
||||
else_source: [Optional] The observable sequence or
|
||||
Promise that will be run if the condition function returns
|
||||
False. If this is not provided, it defaults to
|
||||
reactivex.empty
|
||||
|
||||
Returns:
|
||||
An observable sequence which is either the then_source or
|
||||
else_source.
|
||||
"""
|
||||
|
||||
else_source_: Union[Observable[_T], "Future[_T]"] = else_source or reactivex.empty()
|
||||
|
||||
then_source = (
|
||||
reactivex.from_future(then_source)
|
||||
if isinstance(then_source, Future)
|
||||
else then_source
|
||||
)
|
||||
else_source_ = (
|
||||
reactivex.from_future(else_source_)
|
||||
if isinstance(else_source_, Future)
|
||||
else else_source_
|
||||
)
|
||||
|
||||
def factory(_: abc.SchedulerBase) -> Union[Observable[_T], "Future[_T]"]:
|
||||
return then_source if condition() else else_source_
|
||||
|
||||
return reactivex.defer(factory)
|
||||
|
||||
|
||||
__all__ = ["if_then_"]
|
||||
@@ -0,0 +1,13 @@
|
||||
from typing import Optional
|
||||
|
||||
from reactivex import Observable, abc, timer, typing
|
||||
|
||||
|
||||
def interval_(
|
||||
period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[int]:
|
||||
|
||||
return timer(period, period, scheduler)
|
||||
|
||||
|
||||
__all__ = ["interval_"]
|
||||
@@ -0,0 +1,273 @@
|
||||
import re
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
from reactivex import Notification, Observable, abc, notification, typing
|
||||
from reactivex.disposable import CompositeDisposable, Disposable
|
||||
from reactivex.scheduler import NewThreadScheduler
|
||||
|
||||
new_thread_scheduler = NewThreadScheduler()
|
||||
|
||||
# tokens will be searched in the order below using pipe
|
||||
# group of elements: match any characters surrounded by ()
|
||||
pattern_group = r"(\(.*?\))"
|
||||
# timespan: match one or multiple hyphens
|
||||
pattern_ticks = r"(-+)"
|
||||
# comma err: match any comma which is not in a group
|
||||
pattern_comma_error = r"(,)"
|
||||
# element: match | or # or one or more characters which are not - | # ( ) ,
|
||||
pattern_element = r"(#|\||[^-,()#\|]+)"
|
||||
|
||||
pattern = r"|".join(
|
||||
[
|
||||
pattern_group,
|
||||
pattern_ticks,
|
||||
pattern_comma_error,
|
||||
pattern_element,
|
||||
]
|
||||
)
|
||||
tokens = re.compile(pattern)
|
||||
|
||||
|
||||
def hot(
|
||||
string: str,
|
||||
timespan: typing.RelativeTime = 0.1,
|
||||
duetime: typing.AbsoluteOrRelativeTime = 0.0,
|
||||
lookup: Optional[Mapping[Union[str, float], Any]] = None,
|
||||
error: Optional[Exception] = None,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[Any]:
|
||||
_scheduler = scheduler or new_thread_scheduler
|
||||
|
||||
if isinstance(duetime, datetime):
|
||||
duetime = duetime - _scheduler.now
|
||||
|
||||
messages = parse(
|
||||
string,
|
||||
timespan=timespan,
|
||||
time_shift=duetime,
|
||||
lookup=lookup,
|
||||
error=error,
|
||||
raise_stopped=True,
|
||||
)
|
||||
|
||||
lock = threading.RLock()
|
||||
is_stopped = False
|
||||
observers: List[abc.ObserverBase[Any]] = []
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
# should a hot observable already completed or on error
|
||||
# re-push on_completed/on_error at subscription time?
|
||||
if not is_stopped:
|
||||
with lock:
|
||||
observers.append(observer)
|
||||
|
||||
def dispose() -> None:
|
||||
with lock:
|
||||
try:
|
||||
observers.remove(observer)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return Disposable(dispose)
|
||||
|
||||
def create_action(notification: Notification[Any]) -> typing.ScheduledAction[Any]:
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
nonlocal is_stopped
|
||||
|
||||
with lock:
|
||||
for observer in observers:
|
||||
notification.accept(observer)
|
||||
|
||||
if notification.kind in ("C", "E"):
|
||||
is_stopped = True
|
||||
|
||||
return action
|
||||
|
||||
for message in messages:
|
||||
timespan, notification = message
|
||||
action = create_action(notification)
|
||||
|
||||
# Don't make closures within a loop
|
||||
_scheduler.schedule_relative(timespan, action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def from_marbles(
|
||||
string: str,
|
||||
timespan: typing.RelativeTime = 0.1,
|
||||
lookup: Optional[Mapping[Union[str, float], Any]] = None,
|
||||
error: Optional[Exception] = None,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[Any]:
|
||||
messages = parse(
|
||||
string, timespan=timespan, lookup=lookup, error=error, raise_stopped=True
|
||||
)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or new_thread_scheduler
|
||||
disp = CompositeDisposable()
|
||||
|
||||
def schedule_msg(
|
||||
message: Tuple[typing.RelativeTime, Notification[Any]]
|
||||
) -> None:
|
||||
duetime, notification = message
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
notification.accept(observer)
|
||||
|
||||
disp.add(_scheduler.schedule_relative(duetime, action))
|
||||
|
||||
for message in messages:
|
||||
# Don't make closures within a loop
|
||||
schedule_msg(message)
|
||||
|
||||
return disp
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def parse(
|
||||
string: str,
|
||||
timespan: typing.RelativeTime = 1.0,
|
||||
time_shift: typing.RelativeTime = 0.0,
|
||||
lookup: Optional[Mapping[Union[str, float], Any]] = None,
|
||||
error: Optional[Exception] = None,
|
||||
raise_stopped: bool = False,
|
||||
) -> List[Tuple[typing.RelativeTime, notification.Notification[Any]]]:
|
||||
"""Convert a marble diagram string to a list of messages.
|
||||
|
||||
Each character in the string will advance time by timespan
|
||||
(exept for space). Characters that are not special (see the table below)
|
||||
will be interpreted as a value to be emitted. numbers will be cast
|
||||
to int or float.
|
||||
|
||||
Special characters:
|
||||
+--------+--------------------------------------------------------+
|
||||
| `-` | advance time by timespan |
|
||||
+--------+--------------------------------------------------------+
|
||||
| `#` | on_error() |
|
||||
+--------+--------------------------------------------------------+
|
||||
| `|` | on_completed() |
|
||||
+--------+--------------------------------------------------------+
|
||||
| `(` | open a group of elements sharing the same timestamp |
|
||||
+--------+--------------------------------------------------------+
|
||||
| `)` | close a group of elements |
|
||||
+--------+--------------------------------------------------------+
|
||||
| `,` | separate elements in a group |
|
||||
+--------+--------------------------------------------------------+
|
||||
| space | used to align multiple diagrams, does not advance time |
|
||||
+--------+--------------------------------------------------------+
|
||||
|
||||
In a group of elements, the position of the initial `(` determines the
|
||||
timestamp at which grouped elements will be emitted. E.g. `--(12,3,4)--`
|
||||
will emit 12, 3, 4 at 2 * timespan and then advance virtual time
|
||||
by 8 * timespan.
|
||||
|
||||
Examples:
|
||||
>>> parse("--1--(2,3)-4--|")
|
||||
>>> parse("a--b--c-", lookup={'a': 1, 'b': 2, 'c': 3})
|
||||
>>> parse("a--b---#", error=ValueError("foo"))
|
||||
|
||||
Args:
|
||||
string: String with marble diagram
|
||||
|
||||
timespan: [Optional] duration of each character in second.
|
||||
If not specified, defaults to 0.1s.
|
||||
|
||||
lookup: [Optional] dict used to convert an element into a specified
|
||||
value. If not specified, defaults to {}.
|
||||
|
||||
time_shift: [Optional] time used to delay every elements.
|
||||
If not specified, defaults to 0.0s.
|
||||
|
||||
error: [Optional] exception that will be use in place of the # symbol.
|
||||
If not specified, defaults to Exception('error').
|
||||
|
||||
raise_finished: [optional] raise ValueError if elements are
|
||||
declared after on_completed or on_error symbol.
|
||||
|
||||
Returns:
|
||||
A list of messages defined as a tuple of (timespan, notification).
|
||||
|
||||
"""
|
||||
|
||||
error_ = error or Exception("error")
|
||||
lookup_ = lookup or {}
|
||||
|
||||
if isinstance(timespan, timedelta):
|
||||
timespan = timespan.total_seconds()
|
||||
if isinstance(time_shift, timedelta):
|
||||
time_shift = time_shift.total_seconds()
|
||||
|
||||
string = string.replace(" ", "")
|
||||
|
||||
# try to cast a string to an int, then to a float
|
||||
def try_number(element: str) -> Union[float, str]:
|
||||
try:
|
||||
return int(element)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(element)
|
||||
except ValueError:
|
||||
return element
|
||||
|
||||
def map_element(
|
||||
time: typing.RelativeTime, element: str
|
||||
) -> Tuple[typing.RelativeTime, Notification[Any]]:
|
||||
if element == "|":
|
||||
return (time, notification.OnCompleted())
|
||||
elif element == "#":
|
||||
return (time, notification.OnError(error_))
|
||||
else:
|
||||
value = try_number(element)
|
||||
value = lookup_.get(value, value)
|
||||
return (time, notification.OnNext(value))
|
||||
|
||||
is_stopped = False
|
||||
|
||||
def check_stopped(element: str) -> None:
|
||||
nonlocal is_stopped
|
||||
if raise_stopped:
|
||||
if is_stopped:
|
||||
raise ValueError("Elements cannot be declared after a # or | symbol.")
|
||||
|
||||
if element in ("#", "|"):
|
||||
is_stopped = True
|
||||
|
||||
iframe = 0
|
||||
messages: List[Tuple[typing.RelativeTime, Notification[Any]]] = []
|
||||
|
||||
for results in tokens.findall(string):
|
||||
timestamp = iframe * timespan + time_shift
|
||||
group, ticks, comma_error, element = results
|
||||
|
||||
if group:
|
||||
elements = group[1:-1].split(",")
|
||||
for elm in elements:
|
||||
check_stopped(elm)
|
||||
grp_messages = [
|
||||
map_element(timestamp, elm) for elm in elements if elm != ""
|
||||
]
|
||||
messages.extend(grp_messages)
|
||||
iframe += len(group)
|
||||
|
||||
if ticks:
|
||||
iframe += len(ticks)
|
||||
|
||||
if comma_error:
|
||||
raise ValueError("Comma is only allowed in group of elements.")
|
||||
|
||||
if element:
|
||||
check_stopped(element)
|
||||
message = map_element(timestamp, element)
|
||||
messages.append(message)
|
||||
iframe += len(element)
|
||||
|
||||
return messages
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing import TypeVar
|
||||
|
||||
import reactivex
|
||||
from reactivex import Observable
|
||||
from reactivex import operators as ops
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def merge_(*sources: Observable[_T]) -> Observable[_T]:
|
||||
return reactivex.from_iterable(sources).pipe(ops.merge_all())
|
||||
|
||||
|
||||
__all__ = ["merge_"]
|
||||
@@ -0,0 +1,23 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import Disposable
|
||||
|
||||
|
||||
def never_() -> Observable[Any]:
|
||||
"""Returns a non-terminating observable sequence, which can be used
|
||||
to denote an infinite duration (e.g. when using reactive joins).
|
||||
|
||||
Returns:
|
||||
An observable sequence whose observers will never get called.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
return Disposable()
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["never_"]
|
||||
@@ -0,0 +1,357 @@
|
||||
# By design, pylint: disable=C0302
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
from typing import Any, Callable, Generator, Optional, TypeVar, Union, cast, overload
|
||||
|
||||
from reactivex import abc
|
||||
from reactivex.disposable import Disposable
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
from reactivex.scheduler.eventloop import AsyncIOScheduler
|
||||
|
||||
from ..observer import AutoDetachObserver
|
||||
|
||||
_A = TypeVar("_A")
|
||||
_B = TypeVar("_B")
|
||||
_C = TypeVar("_C")
|
||||
_D = TypeVar("_D")
|
||||
_E = TypeVar("_E")
|
||||
_F = TypeVar("_F")
|
||||
_G = TypeVar("_G")
|
||||
|
||||
_T_out = TypeVar("_T_out", covariant=True)
|
||||
|
||||
|
||||
class Observable(abc.ObservableBase[_T_out]):
|
||||
"""Observable base class.
|
||||
|
||||
Represents a push-style collection, which you can :func:`pipe <pipe>` into
|
||||
:mod:`operators <reactivex.operators>`."""
|
||||
|
||||
def __init__(self, subscribe: Optional[abc.Subscription[_T_out]] = None) -> None:
|
||||
"""Creates an observable sequence object from the specified
|
||||
subscription function.
|
||||
|
||||
Args:
|
||||
subscribe: [Optional] Subscription function
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
self.lock = threading.RLock()
|
||||
self._subscribe = subscribe
|
||||
|
||||
def _subscribe_core(
|
||||
self,
|
||||
observer: abc.ObserverBase[_T_out],
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
return self._subscribe(observer, scheduler) if self._subscribe else Disposable()
|
||||
|
||||
def subscribe(
|
||||
self,
|
||||
on_next: Optional[
|
||||
Union[abc.ObserverBase[_T_out], abc.OnNext[_T_out], None]
|
||||
] = None,
|
||||
on_error: Optional[abc.OnError] = None,
|
||||
on_completed: Optional[abc.OnCompleted] = None,
|
||||
*,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
"""Subscribe an observer to the observable sequence.
|
||||
|
||||
You may subscribe using an observer or callbacks, not both; if the first
|
||||
argument is an instance of :class:`Observer <..abc.ObserverBase>` or if
|
||||
it has a (callable) attribute named :code:`on_next`, then any callback
|
||||
arguments will be ignored.
|
||||
|
||||
Examples:
|
||||
>>> source.subscribe()
|
||||
>>> source.subscribe(observer)
|
||||
>>> source.subscribe(observer, scheduler=scheduler)
|
||||
>>> source.subscribe(on_next)
|
||||
>>> source.subscribe(on_next, on_error)
|
||||
>>> source.subscribe(on_next, on_error, on_completed)
|
||||
>>> source.subscribe(on_next, on_error, on_completed, scheduler=scheduler)
|
||||
|
||||
Args:
|
||||
observer: [Optional] The object that is to receive
|
||||
notifications.
|
||||
on_error: [Optional] Action to invoke upon exceptional termination
|
||||
of the observable sequence.
|
||||
on_completed: [Optional] Action to invoke upon graceful termination
|
||||
of the observable sequence.
|
||||
on_next: [Optional] Action to invoke for each element in the
|
||||
observable sequence.
|
||||
scheduler: [Optional] The default scheduler to use for this
|
||||
subscription.
|
||||
|
||||
Returns:
|
||||
Disposable object representing an observer's subscription to
|
||||
the observable sequence.
|
||||
"""
|
||||
if (
|
||||
isinstance(on_next, abc.ObserverBase)
|
||||
or hasattr(on_next, "on_next")
|
||||
and callable(getattr(on_next, "on_next"))
|
||||
):
|
||||
obv = cast(abc.ObserverBase[_T_out], on_next)
|
||||
on_next = obv.on_next
|
||||
on_error = obv.on_error
|
||||
on_completed = obv.on_completed
|
||||
|
||||
auto_detach_observer: AutoDetachObserver[_T_out] = AutoDetachObserver(
|
||||
on_next, on_error, on_completed
|
||||
)
|
||||
|
||||
def fix_subscriber(
|
||||
subscriber: Union[abc.DisposableBase, Callable[[], None]]
|
||||
) -> abc.DisposableBase:
|
||||
"""Fixes subscriber to make sure it returns a Disposable instead
|
||||
of None or a dispose function"""
|
||||
|
||||
if isinstance(subscriber, abc.DisposableBase) or hasattr(
|
||||
subscriber, "dispose"
|
||||
):
|
||||
# Note: cast can be avoided using Protocols (Python 3.9)
|
||||
return cast(abc.DisposableBase, subscriber)
|
||||
|
||||
return Disposable(subscriber)
|
||||
|
||||
def set_disposable(
|
||||
_: Optional[abc.SchedulerBase] = None, __: Any = None
|
||||
) -> None:
|
||||
try:
|
||||
subscriber = self._subscribe_core(auto_detach_observer, scheduler)
|
||||
except Exception as ex: # By design. pylint: disable=W0703
|
||||
if not auto_detach_observer.fail(ex):
|
||||
raise
|
||||
else:
|
||||
auto_detach_observer.subscription = fix_subscriber(subscriber)
|
||||
|
||||
# Subscribe needs to set up the trampoline before for subscribing.
|
||||
# Actually, the first call to Subscribe creates the trampoline so
|
||||
# that it may assign its disposable before any observer executes
|
||||
# OnNext over the CurrentThreadScheduler. This enables single-
|
||||
# threaded cancellation
|
||||
# https://social.msdn.microsoft.com/Forums/en-US/eb82f593-9684-4e27-
|
||||
# 97b9-8b8886da5c33/whats-the-rationale-behind-how-currentthreadsche
|
||||
# dulerschedulerequired-behaves?forum=rx
|
||||
current_thread_scheduler = CurrentThreadScheduler.singleton()
|
||||
if current_thread_scheduler.schedule_required():
|
||||
current_thread_scheduler.schedule(set_disposable)
|
||||
else:
|
||||
set_disposable()
|
||||
|
||||
# Hide the identity of the auto detach observer
|
||||
return Disposable(auto_detach_observer.dispose)
|
||||
|
||||
@overload
|
||||
def pipe(self, __op1: Callable[[Observable[_T_out]], _A]) -> _A:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
) -> _B:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
__op3: Callable[[_B], _C],
|
||||
) -> _C:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
__op3: Callable[[_B], _C],
|
||||
__op4: Callable[[_C], _D],
|
||||
) -> _D:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
__op3: Callable[[_B], _C],
|
||||
__op4: Callable[[_C], _D],
|
||||
__op5: Callable[[_D], _E],
|
||||
) -> _E:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
__op3: Callable[[_B], _C],
|
||||
__op4: Callable[[_C], _D],
|
||||
__op5: Callable[[_D], _E],
|
||||
__op6: Callable[[_E], _F],
|
||||
) -> _F:
|
||||
...
|
||||
|
||||
@overload
|
||||
def pipe(
|
||||
self,
|
||||
__op1: Callable[[Observable[_T_out]], _A],
|
||||
__op2: Callable[[_A], _B],
|
||||
__op3: Callable[[_B], _C],
|
||||
__op4: Callable[[_C], _D],
|
||||
__op5: Callable[[_D], _E],
|
||||
__op6: Callable[[_E], _F],
|
||||
__op7: Callable[[_F], _G],
|
||||
) -> _G:
|
||||
...
|
||||
|
||||
def pipe(self, *operators: Callable[[Any], Any]) -> Any:
|
||||
"""Compose multiple operators left to right.
|
||||
|
||||
Composes zero or more operators into a functional composition.
|
||||
The operators are composed from left to right. A composition of zero
|
||||
operators gives back the original source.
|
||||
|
||||
Examples:
|
||||
>>> source.pipe() == source
|
||||
>>> source.pipe(f) == f(source)
|
||||
>>> source.pipe(g, f) == f(g(source))
|
||||
>>> source.pipe(h, g, f) == f(g(h(source)))
|
||||
|
||||
Args:
|
||||
operators: Sequence of operators.
|
||||
|
||||
Returns:
|
||||
The composed observable.
|
||||
"""
|
||||
from ..pipe import pipe as pipe_
|
||||
|
||||
return pipe_(self, *operators)
|
||||
|
||||
def run(self) -> Any:
|
||||
"""Run source synchronously.
|
||||
|
||||
Subscribes to the observable source. Then blocks and waits for the
|
||||
observable source to either complete or error. Returns the
|
||||
last value emitted, or throws exception if any error occurred.
|
||||
|
||||
Examples:
|
||||
>>> result = run(source)
|
||||
|
||||
Raises:
|
||||
SequenceContainsNoElementsError: if observable completes
|
||||
(on_completed) without any values being emitted.
|
||||
Exception: raises exception if any error (on_error) occurred.
|
||||
|
||||
Returns:
|
||||
The last element emitted from the observable.
|
||||
"""
|
||||
from ..run import run
|
||||
|
||||
return run(self)
|
||||
|
||||
def __await__(self) -> Generator[Any, None, _T_out]:
|
||||
"""Awaits the given observable.
|
||||
|
||||
Returns:
|
||||
The last item of the observable sequence.
|
||||
"""
|
||||
from ..operators._tofuture import to_future_
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
future: asyncio.Future[_T_out] = self.pipe(
|
||||
to_future_(scheduler=AsyncIOScheduler(loop=loop))
|
||||
)
|
||||
return future.__await__()
|
||||
|
||||
def __add__(self, other: Observable[_T_out]) -> Observable[_T_out]:
|
||||
"""Pythonic version of :func:`concat <reactivex.concat>`.
|
||||
|
||||
Example:
|
||||
>>> zs = xs + ys
|
||||
|
||||
Args:
|
||||
other: The second observable sequence in the concatenation.
|
||||
|
||||
Returns:
|
||||
Concatenated observable sequence.
|
||||
"""
|
||||
from reactivex import concat
|
||||
|
||||
return concat(self, other)
|
||||
|
||||
def __iadd__(self, other: Observable[_T_out]) -> "Observable[_T_out]":
|
||||
"""Pythonic use of :func:`concat <reactivex.concat>`.
|
||||
|
||||
Example:
|
||||
>>> xs += ys
|
||||
|
||||
Args:
|
||||
other: The second observable sequence in the concatenation.
|
||||
|
||||
Returns:
|
||||
Concatenated observable sequence.
|
||||
"""
|
||||
from reactivex import concat
|
||||
|
||||
return concat(self, other)
|
||||
|
||||
def __getitem__(self, key: Union[slice, int]) -> Observable[_T_out]:
|
||||
"""
|
||||
Pythonic version of :func:`slice <reactivex.operators.slice>`.
|
||||
|
||||
Slices the given observable using Python slice notation. The arguments
|
||||
to slice are `start`, `stop` and `step` given within brackets `[]` and
|
||||
separated by the colons `:`.
|
||||
|
||||
It is basically a wrapper around the operators
|
||||
:func:`skip <reactivex.operators.skip>`,
|
||||
:func:`skip_last <reactivex.operators.skip_last>`,
|
||||
:func:`take <reactivex.operators.take>`,
|
||||
:func:`take_last <reactivex.operators.take_last>` and
|
||||
:func:`filter <reactivex.operators.filter>`.
|
||||
|
||||
The following diagram helps you remember how slices works with streams.
|
||||
Positive numbers are relative to the start of the events, while negative
|
||||
numbers are relative to the end (close) of the stream.
|
||||
|
||||
.. code::
|
||||
|
||||
r---e---a---c---t---i---v---e---!
|
||||
0 1 2 3 4 5 6 7 8
|
||||
-8 -7 -6 -5 -4 -3 -2 -1 0
|
||||
|
||||
Examples:
|
||||
>>> result = source[1:10]
|
||||
>>> result = source[1:-2]
|
||||
>>> result = source[1:-1:2]
|
||||
|
||||
Args:
|
||||
key: Slice object
|
||||
|
||||
Returns:
|
||||
Sliced observable sequence.
|
||||
|
||||
Raises:
|
||||
TypeError: If key is not of type :code:`int` or :code:`slice`
|
||||
"""
|
||||
|
||||
if isinstance(key, slice):
|
||||
start, stop, step = key.start, key.stop, key.step
|
||||
else:
|
||||
start, stop, step = key, key + 1, 1
|
||||
|
||||
from ..operators._slice import slice_
|
||||
|
||||
return slice_(start, stop, step)(self)
|
||||
|
||||
|
||||
__all__ = ["Observable"]
|
||||
@@ -0,0 +1,73 @@
|
||||
from asyncio import Future
|
||||
from typing import Callable, Optional, TypeVar, Union
|
||||
|
||||
import reactivex
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import (
|
||||
CompositeDisposable,
|
||||
SerialDisposable,
|
||||
SingleAssignmentDisposable,
|
||||
)
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def on_error_resume_next_(
|
||||
*sources: Union[
|
||||
Observable[_T], "Future[_T]", Callable[[Optional[Exception]], Observable[_T]]
|
||||
]
|
||||
) -> Observable[_T]:
|
||||
"""Continues an observable sequence that is terminated normally or
|
||||
by an exception with the next observable sequence.
|
||||
|
||||
Examples:
|
||||
>>> res = reactivex.on_error_resume_next(xs, ys, zs)
|
||||
|
||||
Returns:
|
||||
An observable sequence that concatenates the source sequences,
|
||||
even if a sequence terminates exceptionally.
|
||||
"""
|
||||
|
||||
sources_ = iter(sources)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
scheduler = scheduler or CurrentThreadScheduler.singleton()
|
||||
|
||||
subscription = SerialDisposable()
|
||||
cancelable = SerialDisposable()
|
||||
|
||||
def action(
|
||||
scheduler: abc.SchedulerBase, state: Optional[Exception] = None
|
||||
) -> None:
|
||||
try:
|
||||
source = next(sources_)
|
||||
except StopIteration:
|
||||
observer.on_completed()
|
||||
return
|
||||
|
||||
# Allow source to be a factory method taking an error
|
||||
source = source(state) if callable(source) else source
|
||||
current = (
|
||||
reactivex.from_future(source) if isinstance(source, Future) else source
|
||||
)
|
||||
|
||||
d = SingleAssignmentDisposable()
|
||||
subscription.disposable = d
|
||||
|
||||
def on_resume(state: Optional[Exception] = None) -> None:
|
||||
scheduler.schedule(action, state)
|
||||
|
||||
d.disposable = current.subscribe(
|
||||
observer.on_next, on_resume, on_resume, scheduler=scheduler
|
||||
)
|
||||
|
||||
cancelable.disposable = scheduler.schedule(action)
|
||||
return CompositeDisposable(subscription, cancelable)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["on_error_resume_next_"]
|
||||
@@ -0,0 +1,70 @@
|
||||
from sys import maxsize
|
||||
from typing import Iterator, Optional
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import MultipleAssignmentDisposable
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
|
||||
def range_(
|
||||
start: int,
|
||||
stop: Optional[int] = None,
|
||||
step: Optional[int] = None,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[int]:
|
||||
"""Generates an observable sequence of integral numbers within a
|
||||
specified range, using the specified scheduler to send out observer
|
||||
messages.
|
||||
|
||||
Examples:
|
||||
>>> res = range(10)
|
||||
>>> res = range(0, 10)
|
||||
>>> res = range(0, 10, 1)
|
||||
|
||||
Args:
|
||||
start: The value of the first integer in the sequence.
|
||||
stop: [Optional] Generate number up to (exclusive) the stop
|
||||
value. Default is `sys.maxsize`.
|
||||
step: [Optional] The step to be used (default is 1).
|
||||
scheduler: The scheduler to schedule the values on.
|
||||
|
||||
Returns:
|
||||
An observable sequence that contains a range of sequential
|
||||
integral numbers.
|
||||
"""
|
||||
|
||||
_stop: int = maxsize if stop is None else stop
|
||||
_step: int = 1 if step is None else step
|
||||
|
||||
if step is None and stop is None:
|
||||
range_t = range(start)
|
||||
elif step is None:
|
||||
range_t = range(start, _stop)
|
||||
else:
|
||||
range_t = range(start, _stop, _step)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
nonlocal range_t
|
||||
|
||||
_scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton()
|
||||
sd = MultipleAssignmentDisposable()
|
||||
|
||||
def action(
|
||||
scheduler: abc.SchedulerBase, iterator: Optional[Iterator[int]]
|
||||
) -> None:
|
||||
try:
|
||||
assert iterator
|
||||
observer.on_next(next(iterator))
|
||||
sd.disposable = _scheduler.schedule(action, state=iterator)
|
||||
except StopIteration:
|
||||
observer.on_completed()
|
||||
|
||||
sd.disposable = _scheduler.schedule(action, iter(range_t))
|
||||
return sd
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["range_"]
|
||||
@@ -0,0 +1,35 @@
|
||||
from typing import Optional, TypeVar
|
||||
|
||||
import reactivex
|
||||
from reactivex import Observable
|
||||
from reactivex import operators as ops
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def repeat_value_(value: _T, repeat_count: Optional[int] = None) -> Observable[_T]:
|
||||
"""Generates an observable sequence that repeats the given element
|
||||
the specified number of times.
|
||||
|
||||
Examples:
|
||||
1 - res = repeat_value(42)
|
||||
2 - res = repeat_value(42, 4)
|
||||
|
||||
Args:
|
||||
value: Element to repeat.
|
||||
repeat_count: [Optional] Number of times to repeat the element.
|
||||
If not specified, repeats indefinitely.
|
||||
|
||||
Returns:
|
||||
An observable sequence that repeats the given element the
|
||||
specified number of times.
|
||||
"""
|
||||
|
||||
if repeat_count == -1:
|
||||
repeat_count = None
|
||||
|
||||
xs = reactivex.return_value(value)
|
||||
return xs.pipe(ops.repeat(repeat_count))
|
||||
|
||||
|
||||
__all__ = ["repeat_value_"]
|
||||
@@ -0,0 +1,64 @@
|
||||
from typing import Any, Callable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.scheduler import CurrentThreadScheduler
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def return_value_(
|
||||
value: _T, scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[_T]:
|
||||
"""Returns an observable sequence that contains a single element,
|
||||
using the specified scheduler to send out observer messages.
|
||||
There is an alias called 'just'.
|
||||
|
||||
Examples:
|
||||
>>> res = return(42)
|
||||
>>> res = return(42, rx.Scheduler.timeout)
|
||||
|
||||
Args:
|
||||
value: Single element in the resulting observable sequence.
|
||||
|
||||
Returns:
|
||||
An observable sequence containing the single specified
|
||||
element.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton()
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
observer.on_next(value)
|
||||
observer.on_completed()
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def from_callable_(
|
||||
supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[_T]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton()
|
||||
|
||||
def action(_: abc.SchedulerBase, __: Any = None) -> None:
|
||||
nonlocal observer
|
||||
|
||||
try:
|
||||
observer.on_next(supplier())
|
||||
observer.on_completed()
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
observer.on_error(e)
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["return_value_", "from_callable_"]
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import Callable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc, to_async
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def start_(
|
||||
func: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[_T]:
|
||||
"""Invokes the specified function asynchronously on the specified
|
||||
scheduler, surfacing the result through an observable sequence.
|
||||
|
||||
Example:
|
||||
>>> res = reactivex.start(lambda: pprint('hello'))
|
||||
>>> res = reactivex.start(lambda: pprint('hello'), rx.Scheduler.timeout)
|
||||
|
||||
Args:
|
||||
func: Function to run asynchronously.
|
||||
scheduler: [Optional] Scheduler to run the function on. If
|
||||
not specified, defaults to Scheduler.timeout.
|
||||
|
||||
Remarks:
|
||||
The function is called immediately, not during the subscription
|
||||
of the resulting sequence. Multiple subscriptions to the
|
||||
resulting sequence can observe the function's result.
|
||||
|
||||
Returns:
|
||||
An observable sequence exposing the function's result value,
|
||||
or an exception.
|
||||
"""
|
||||
|
||||
return to_async(func, scheduler)()
|
||||
|
||||
|
||||
__all__ = ["start_"]
|
||||
@@ -0,0 +1,18 @@
|
||||
from asyncio import Future
|
||||
from typing import Callable, TypeVar
|
||||
|
||||
from reactivex import Observable, from_future, throw
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def start_async_(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]:
|
||||
try:
|
||||
future = function_async()
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
return throw(ex)
|
||||
|
||||
return from_future(future)
|
||||
|
||||
|
||||
__all__ = ["start_async_"]
|
||||
@@ -0,0 +1,25 @@
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.scheduler import ImmediateScheduler
|
||||
|
||||
|
||||
def throw_(
|
||||
exception: Union[str, Exception], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[Any]:
|
||||
exception_ = exception if isinstance(exception, Exception) else Exception(exception)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or ImmediateScheduler.singleton()
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
|
||||
observer.on_error(exception_)
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["throw_"]
|
||||
134
myenv/lib/python3.12/site-packages/reactivex/observable/timer.py
Normal file
134
myenv/lib/python3.12/site-packages/reactivex/observable/timer.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from reactivex import Observable, abc, typing
|
||||
from reactivex.disposable import MultipleAssignmentDisposable
|
||||
from reactivex.scheduler import TimeoutScheduler
|
||||
from reactivex.scheduler.periodicscheduler import PeriodicScheduler
|
||||
|
||||
|
||||
def observable_timer_date(
|
||||
duetime: typing.AbsoluteTime, scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[int]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler: abc.SchedulerBase = (
|
||||
scheduler or scheduler_ or TimeoutScheduler.singleton()
|
||||
)
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
|
||||
observer.on_next(0)
|
||||
observer.on_completed()
|
||||
|
||||
return _scheduler.schedule_absolute(duetime, action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def observable_timer_duetime_and_period(
|
||||
duetime: typing.AbsoluteOrRelativeTime,
|
||||
period: typing.AbsoluteOrRelativeTime,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[int]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton()
|
||||
nonlocal duetime
|
||||
|
||||
if not isinstance(duetime, datetime):
|
||||
duetime = _scheduler.now + _scheduler.to_timedelta(duetime)
|
||||
|
||||
p = max(0.0, _scheduler.to_seconds(period))
|
||||
mad = MultipleAssignmentDisposable()
|
||||
dt = duetime
|
||||
count = 0
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
|
||||
nonlocal dt
|
||||
nonlocal count
|
||||
|
||||
if p > 0.0:
|
||||
now = scheduler.now
|
||||
dt = dt + scheduler.to_timedelta(p)
|
||||
if dt <= now:
|
||||
dt = now + scheduler.to_timedelta(p)
|
||||
|
||||
observer.on_next(count)
|
||||
count += 1
|
||||
mad.disposable = scheduler.schedule_absolute(dt, action)
|
||||
|
||||
mad.disposable = _scheduler.schedule_absolute(dt, action)
|
||||
return mad
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def observable_timer_timespan(
|
||||
duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Observable[int]:
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton()
|
||||
d = _scheduler.to_seconds(duetime)
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
|
||||
observer.on_next(0)
|
||||
observer.on_completed()
|
||||
|
||||
if d <= 0.0:
|
||||
return _scheduler.schedule(action)
|
||||
return _scheduler.schedule_relative(d, action)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
def observable_timer_timespan_and_period(
|
||||
duetime: typing.RelativeTime,
|
||||
period: typing.RelativeTime,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[int]:
|
||||
if duetime == period:
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[int],
|
||||
scheduler_: Optional[abc.SchedulerBase] = None,
|
||||
) -> abc.DisposableBase:
|
||||
_scheduler: abc.SchedulerBase = (
|
||||
scheduler or scheduler_ or TimeoutScheduler.singleton()
|
||||
)
|
||||
|
||||
def action(count: Optional[int] = None) -> Optional[int]:
|
||||
if count is not None:
|
||||
observer.on_next(count)
|
||||
return count + 1
|
||||
return None
|
||||
|
||||
if not isinstance(_scheduler, PeriodicScheduler):
|
||||
raise ValueError("Sceduler must be PeriodicScheduler")
|
||||
return _scheduler.schedule_periodic(period, action, state=0)
|
||||
|
||||
return Observable(subscribe)
|
||||
return observable_timer_duetime_and_period(duetime, period, scheduler)
|
||||
|
||||
|
||||
def timer_(
|
||||
duetime: typing.AbsoluteOrRelativeTime,
|
||||
period: Optional[typing.RelativeTime] = None,
|
||||
scheduler: Optional[abc.SchedulerBase] = None,
|
||||
) -> Observable[int]:
|
||||
if isinstance(duetime, datetime):
|
||||
if period is None:
|
||||
return observable_timer_date(duetime, scheduler)
|
||||
else:
|
||||
return observable_timer_duetime_and_period(duetime, period, scheduler)
|
||||
|
||||
if period is None:
|
||||
return observable_timer_timespan(duetime, scheduler)
|
||||
|
||||
return observable_timer_timespan_and_period(duetime, period, scheduler)
|
||||
|
||||
|
||||
__all__ = ["timer_"]
|
||||
@@ -0,0 +1,54 @@
|
||||
from typing import Any, Callable, Optional, TypeVar
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex import operators as ops
|
||||
from reactivex.scheduler import TimeoutScheduler
|
||||
from reactivex.subject import AsyncSubject
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def to_async_(
|
||||
func: Callable[..., _T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> Callable[..., Observable[_T]]:
|
||||
"""Converts the function into an asynchronous function. Each
|
||||
invocation of the resulting asynchronous function causes an
|
||||
invocation of the original synchronous function on the specified
|
||||
scheduler.
|
||||
|
||||
Examples:
|
||||
res = reactivex.to_async(lambda x, y: x + y)(4, 3)
|
||||
res = reactivex.to_async(lambda x, y: x + y, Scheduler.timeout)(4, 3)
|
||||
res = reactivex.to_async(lambda x: log.debug(x), Scheduler.timeout)('hello')
|
||||
|
||||
Args:
|
||||
func: Function to convert to an asynchronous function.
|
||||
scheduler: [Optional] Scheduler to run the function on. If not
|
||||
specified, defaults to Scheduler.timeout.
|
||||
|
||||
Returns:
|
||||
Aynchronous function.
|
||||
"""
|
||||
|
||||
_scheduler = scheduler or TimeoutScheduler.singleton()
|
||||
|
||||
def wrapper(*args: Any) -> Observable[_T]:
|
||||
subject: AsyncSubject[_T] = AsyncSubject()
|
||||
|
||||
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
|
||||
try:
|
||||
result = func(*args)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
subject.on_error(ex)
|
||||
return
|
||||
|
||||
subject.on_next(result)
|
||||
subject.on_completed()
|
||||
|
||||
_scheduler.schedule(action)
|
||||
return subject.pipe(ops.as_observable())
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
__all__ = ["to_async_"]
|
||||
@@ -0,0 +1,53 @@
|
||||
from typing import Callable, Optional, TypeVar
|
||||
|
||||
import reactivex
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import CompositeDisposable, Disposable
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def using_(
|
||||
resource_factory: Callable[[], abc.DisposableBase],
|
||||
observable_factory: Callable[[abc.DisposableBase], Observable[_T]],
|
||||
) -> Observable[_T]:
|
||||
"""Constructs an observable sequence that depends on a resource
|
||||
object, whose lifetime is tied to the resulting observable
|
||||
sequence's lifetime.
|
||||
|
||||
Example:
|
||||
>>> res = reactivex.using(lambda: AsyncSubject(), lambda: s: s)
|
||||
|
||||
Args:
|
||||
resource_factory: Factory function to obtain a resource object.
|
||||
observable_factory: Factory function to obtain an observable
|
||||
sequence that depends on the obtained resource.
|
||||
|
||||
Returns:
|
||||
An observable sequence whose lifetime controls the lifetime
|
||||
of the dependent resource object.
|
||||
"""
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
disp: abc.DisposableBase = Disposable()
|
||||
|
||||
try:
|
||||
resource = resource_factory()
|
||||
if resource is not None:
|
||||
disp = resource
|
||||
|
||||
source = observable_factory(resource)
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
d = reactivex.throw(exception).subscribe(observer, scheduler=scheduler)
|
||||
return CompositeDisposable(d, disp)
|
||||
|
||||
return CompositeDisposable(
|
||||
source.subscribe(observer, scheduler=scheduler), disp
|
||||
)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["using_"]
|
||||
@@ -0,0 +1,59 @@
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from reactivex import Observable, abc
|
||||
from reactivex.disposable import CompositeDisposable, SingleAssignmentDisposable
|
||||
from reactivex.internal.utils import NotSet
|
||||
|
||||
|
||||
def with_latest_from_(
|
||||
parent: Observable[Any], *sources: Observable[Any]
|
||||
) -> Observable[Tuple[Any, ...]]:
|
||||
NO_VALUE = NotSet()
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> abc.DisposableBase:
|
||||
def subscribeall(
|
||||
parent: Observable[Any], *children: Observable[Any]
|
||||
) -> List[SingleAssignmentDisposable]:
|
||||
|
||||
values = [NO_VALUE for _ in children]
|
||||
|
||||
def subscribechild(
|
||||
i: int, child: Observable[Any]
|
||||
) -> SingleAssignmentDisposable:
|
||||
subscription = SingleAssignmentDisposable()
|
||||
|
||||
def on_next(value: Any) -> None:
|
||||
with parent.lock:
|
||||
values[i] = value
|
||||
|
||||
subscription.disposable = child.subscribe(
|
||||
on_next, observer.on_error, scheduler=scheduler
|
||||
)
|
||||
return subscription
|
||||
|
||||
parent_subscription = SingleAssignmentDisposable()
|
||||
|
||||
def on_next(value: Any) -> None:
|
||||
with parent.lock:
|
||||
if NO_VALUE not in values:
|
||||
result = (value,) + tuple(values)
|
||||
observer.on_next(result)
|
||||
|
||||
children_subscription = [
|
||||
subscribechild(i, child) for i, child in enumerate(children)
|
||||
]
|
||||
disp = parent.subscribe(
|
||||
on_next, observer.on_error, observer.on_completed, scheduler=scheduler
|
||||
)
|
||||
parent_subscription.disposable = disp
|
||||
|
||||
return [parent_subscription] + children_subscription
|
||||
|
||||
return CompositeDisposable(subscribeall(parent, *sources))
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["with_latest_from_"]
|
||||
@@ -0,0 +1,90 @@
|
||||
from asyncio import Future
|
||||
from threading import RLock
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from reactivex import Observable, abc, from_future
|
||||
from reactivex.disposable import CompositeDisposable, SingleAssignmentDisposable
|
||||
from reactivex.internal import synchronized
|
||||
|
||||
|
||||
def zip_(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]:
|
||||
"""Merges the specified observable sequences into one observable
|
||||
sequence by creating a tuple whenever all of the
|
||||
observable sequences have produced an element at a corresponding
|
||||
index.
|
||||
|
||||
Example:
|
||||
>>> res = zip(obs1, obs2)
|
||||
|
||||
Args:
|
||||
args: Observable sources to zip.
|
||||
|
||||
Returns:
|
||||
An observable sequence containing the result of combining
|
||||
elements of the sources as tuple.
|
||||
"""
|
||||
|
||||
sources = list(args)
|
||||
|
||||
def subscribe(
|
||||
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
|
||||
) -> CompositeDisposable:
|
||||
n = len(sources)
|
||||
queues: List[List[Any]] = [[] for _ in range(n)]
|
||||
lock = RLock()
|
||||
is_completed = [False] * n
|
||||
|
||||
@synchronized(lock)
|
||||
def next_(i: int) -> None:
|
||||
if all(len(q) for q in queues):
|
||||
try:
|
||||
queued_values = [x.pop(0) for x in queues]
|
||||
res = tuple(queued_values)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
observer.on_error(ex)
|
||||
return
|
||||
|
||||
observer.on_next(res)
|
||||
|
||||
# after sending the zipped values, complete the observer if at least one
|
||||
# upstream observable is completed and its queue has length zero
|
||||
if any(
|
||||
(
|
||||
done
|
||||
for queue, done in zip(queues, is_completed)
|
||||
if len(queue) == 0
|
||||
)
|
||||
):
|
||||
observer.on_completed()
|
||||
|
||||
def completed(i: int) -> None:
|
||||
is_completed[i] = True
|
||||
if len(queues[i]) == 0:
|
||||
observer.on_completed()
|
||||
|
||||
subscriptions: List[Optional[abc.DisposableBase]] = [None] * n
|
||||
|
||||
def func(i: int) -> None:
|
||||
source: Observable[Any] = sources[i]
|
||||
if isinstance(source, Future):
|
||||
source = from_future(source)
|
||||
|
||||
sad = SingleAssignmentDisposable()
|
||||
|
||||
def on_next(x: Any) -> None:
|
||||
queues[i].append(x)
|
||||
next_(i)
|
||||
|
||||
sad.disposable = source.subscribe(
|
||||
on_next, observer.on_error, lambda: completed(i), scheduler=scheduler
|
||||
)
|
||||
subscriptions[i] = sad
|
||||
|
||||
for idx in range(n):
|
||||
func(idx)
|
||||
return CompositeDisposable(subscriptions)
|
||||
|
||||
return Observable(subscribe)
|
||||
|
||||
|
||||
__all__ = ["zip_"]
|
||||
Reference in New Issue
Block a user