From cc3f73f3fb47b99a3ead5339ff6be15289b2705d Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 2 Feb 2022 19:53:30 +0100 Subject: [PATCH 001/103] [WIP] Modern type hints - Use modern type-hints (PEP-484) so we can use pylance/pyright at strict settings - Use black formatting --- examples/timeflies/timeflies_tkinter.py | 30 +- rx/__init__.py | 202 ++++--- rx/core/__init__.py | 8 +- rx/core/abc/__init__.py | 29 +- rx/core/abc/asyncobserver.py | 1 + rx/core/abc/disposable.py | 21 +- rx/core/abc/observable.py | 33 +- rx/core/abc/observer.py | 39 +- rx/core/abc/periodicscheduler.py | 36 +- rx/core/abc/scheduler.py | 113 +++- rx/core/abc/startable.py | 3 +- rx/core/abc/subject.py | 55 +- rx/core/notification.py | 98 ++-- rx/core/observable/__init__.py | 4 +- rx/core/observable/amb.py | 14 +- rx/core/observable/case.py | 26 +- rx/core/observable/catch.py | 20 +- rx/core/observable/combinelatest.py | 27 +- rx/core/observable/concat.py | 19 +- rx/core/observable/connectableobservable.py | 23 +- rx/core/observable/defer.py | 11 +- rx/core/observable/empty.py | 11 +- rx/core/observable/forkjoin.py | 13 +- rx/core/observable/fromcallback.py | 10 +- rx/core/observable/fromfuture.py | 20 +- rx/core/observable/fromiterable.py | 15 +- rx/core/observable/generate.py | 2 +- .../observable/generatewithrelativetime.py | 4 +- rx/core/observable/ifthen.py | 7 +- rx/core/observable/interval.py | 9 +- rx/core/observable/marbles.py | 84 +-- rx/core/observable/never.py | 11 +- rx/core/observable/observable.py | 236 +++++---- rx/core/observable/onerrorresumenext.py | 7 +- rx/core/observable/range.py | 24 +- rx/core/observable/returnvalue.py | 25 +- rx/core/observable/start.py | 11 +- rx/core/observable/startasync.py | 4 +- rx/core/observable/throw.py | 11 +- rx/core/observable/timer.py | 63 ++- rx/core/observable/toasync.py | 8 +- rx/core/observable/using.py | 10 +- rx/core/observable/withlatestfrom.py | 3 +- rx/core/observable/zip.py | 25 +- rx/core/observer/__init__.py | 4 +- rx/core/observer/autodetachobserver.py | 26 +- rx/core/observer/observer.py | 49 +- rx/core/observer/scheduledobserver.py | 15 +- rx/core/operators/amb.py | 14 +- rx/core/operators/asobservable.py | 1 + rx/core/operators/buffer.py | 2 +- rx/core/operators/bufferwithtime.py | 14 +- rx/core/operators/catch.py | 22 +- rx/core/operators/combinelatest.py | 2 +- rx/core/operators/connectable/refcount.py | 2 +- rx/core/operators/count.py | 4 +- rx/core/operators/debounce.py | 29 +- rx/core/operators/defaultifempty.py | 7 +- rx/core/operators/delay.py | 36 +- rx/core/operators/delaysubscription.py | 11 +- rx/core/operators/delaywithmapper.py | 13 +- rx/core/operators/dematerialize.py | 1 + rx/core/operators/distinct.py | 3 +- rx/core/operators/distinctuntilchanged.py | 5 +- rx/core/operators/do.py | 28 +- rx/core/operators/dowhile.py | 2 +- rx/core/operators/elementatordefault.py | 1 + rx/core/operators/expand.py | 3 +- rx/core/operators/filter.py | 32 +- rx/core/operators/finallyaction.py | 2 +- rx/core/operators/find.py | 1 + rx/core/operators/first.py | 1 + rx/core/operators/firstordefault.py | 32 +- rx/core/operators/flatmap.py | 48 +- rx/core/operators/groupbyuntil.py | 9 +- rx/core/operators/groupjoin.py | 5 +- rx/core/operators/isempty.py | 13 +- rx/core/operators/join.py | 6 +- rx/core/operators/last.py | 18 +- rx/core/operators/lastordefault.py | 30 +- rx/core/operators/map.py | 32 +- rx/core/operators/materialize.py | 3 +- rx/core/operators/maxby.py | 1 + rx/core/operators/min.py | 2 +- rx/core/operators/multicast.py | 10 +- rx/core/operators/observeon.py | 10 +- rx/core/operators/partition.py | 2 +- rx/core/operators/publish.py | 2 +- rx/core/operators/publishvalue.py | 2 +- rx/core/operators/reduce.py | 14 +- rx/core/operators/replay.py | 19 +- rx/core/operators/sample.py | 14 +- rx/core/operators/scan.py | 31 +- rx/core/operators/sequenceequal.py | 2 +- rx/core/operators/singleordefault.py | 4 +- rx/core/operators/skiplast.py | 22 +- rx/core/operators/skiplastwithtime.py | 9 +- rx/core/operators/skipuntil.py | 28 +- rx/core/operators/skipuntilwithtime.py | 30 +- rx/core/operators/skipwhile.py | 24 +- rx/core/operators/skipwithtime.py | 24 +- rx/core/operators/slice.py | 21 +- rx/core/operators/some.py | 18 +- rx/core/operators/startswith.py | 13 +- rx/core/operators/statistics.py | 3 +- rx/core/operators/subscribeon.py | 23 +- rx/core/operators/sum.py | 15 +- rx/core/operators/switchlatest.py | 5 +- rx/core/operators/takelast.py | 1 + rx/core/operators/takelastbuffer.py | 1 + rx/core/operators/takelastwithtime.py | 11 +- rx/core/operators/takeuntil.py | 4 +- rx/core/operators/takeuntilwithtime.py | 11 +- rx/core/operators/takewithtime.py | 9 +- rx/core/operators/throttlefirst.py | 12 +- rx/core/operators/timeinterval.py | 9 +- rx/core/operators/timeout.py | 23 +- rx/core/operators/timeoutwithmapper.py | 5 +- rx/core/operators/timestamp.py | 11 +- rx/core/operators/todict.py | 13 +- rx/core/operators/tofuture.py | 11 +- rx/core/operators/toiterable.py | 1 + rx/core/operators/tomarbles.py | 12 +- rx/core/operators/toset.py | 1 + rx/core/operators/whiledo.py | 6 +- rx/core/operators/window.py | 7 +- rx/core/operators/windowwithcount.py | 6 +- rx/core/operators/windowwithtime.py | 19 +- rx/core/operators/windowwithtimeorcount.py | 14 +- rx/core/operators/withlatestfrom.py | 1 - rx/core/operators/zip.py | 1 + rx/core/pipe.py | 86 ++- rx/core/run.py | 2 +- rx/core/typing.py | 371 ++----------- rx/disposable/__init__.py | 6 +- rx/disposable/booleandisposable.py | 5 +- rx/disposable/compositedisposable.py | 19 +- rx/disposable/disposable.py | 10 +- rx/disposable/multipleassignmentdisposable.py | 12 +- rx/disposable/refcountdisposable.py | 20 +- rx/disposable/scheduleddisposable.py | 10 +- rx/disposable/serialdisposable.py | 16 +- rx/disposable/singleassignmentdisposable.py | 20 +- rx/internal/__init__.py | 8 +- rx/internal/basic.py | 12 +- rx/internal/exceptions.py | 19 +- rx/internal/priorityqueue.py | 18 +- rx/internal/utils.py | 21 +- rx/operators/__init__.py | 497 +++++++++++------- rx/scheduler/__init__.py | 3 +- rx/scheduler/catchscheduler.py | 65 +-- rx/scheduler/currentthreadscheduler.py | 3 +- rx/scheduler/eventloop/asyncioscheduler.py | 39 +- .../eventloop/asynciothreadsafescheduler.py | 48 +- rx/scheduler/eventloop/eventletscheduler.py | 37 +- rx/scheduler/eventloop/geventscheduler.py | 36 +- rx/scheduler/eventloop/ioloopscheduler.py | 39 +- rx/scheduler/eventloop/twistedscheduler.py | 37 +- rx/scheduler/eventloopscheduler.py | 49 +- rx/scheduler/historicalscheduler.py | 1 + rx/scheduler/immediatescheduler.py | 33 +- rx/scheduler/mainloop/__init__.py | 2 +- rx/scheduler/mainloop/gtkscheduler.py | 78 +-- rx/scheduler/mainloop/pygamescheduler.py | 35 +- rx/scheduler/mainloop/qtscheduler.py | 61 +-- rx/scheduler/mainloop/tkinterscheduler.py | 36 +- rx/scheduler/mainloop/wxscheduler.py | 82 ++- rx/scheduler/newthreadscheduler.py | 43 +- rx/scheduler/periodicscheduler.py | 44 +- rx/scheduler/scheduleditem.py | 22 +- rx/scheduler/scheduler.py | 38 +- rx/scheduler/threadpoolscheduler.py | 10 +- rx/scheduler/timeoutscheduler.py | 36 +- rx/scheduler/trampoline.py | 3 +- rx/scheduler/trampolinescheduler.py | 36 +- rx/scheduler/virtualtimescheduler.py | 38 +- rx/subject/__init__.py | 4 +- rx/subject/asyncsubject.py | 23 +- rx/subject/behaviorsubject.py | 2 +- rx/subject/innersubscription.py | 10 +- rx/subject/replaysubject.py | 42 +- rx/subject/subject.py | 21 +- rx/testing/__init__.py | 5 +- rx/testing/coldobservable.py | 12 +- rx/testing/hotobservable.py | 18 +- rx/testing/marbles.py | 8 +- rx/testing/mockobserver.py | 9 +- rx/testing/reactivetest.py | 6 +- rx/testing/recorded.py | 1 + rx/testing/testscheduler.py | 13 +- tests/test_core/test_notification.py | 68 ++- .../test_connectableobservable.py | 73 +-- tests/test_observable/test_publish.py | 145 +++-- tests/test_observable_multiple.py | 3 +- 194 files changed, 2787 insertions(+), 2270 deletions(-) diff --git a/examples/timeflies/timeflies_tkinter.py b/examples/timeflies/timeflies_tkinter.py index 521c15bf1..590cf25d1 100644 --- a/examples/timeflies/timeflies_tkinter.py +++ b/examples/timeflies/timeflies_tkinter.py @@ -1,7 +1,9 @@ +from typing import Any, Tuple from tkinter import Tk, Label, Frame +import tkinter import rx -from rx import operators as ops +from rx import operators as ops, Observable from rx.subject import Subject from rx.scheduler.mainloop import TkinterScheduler @@ -11,40 +13,34 @@ def main(): root.title("Rx for Python rocks") scheduler = TkinterScheduler(root) - mousemove = Subject() + mousemove: Subject[tkinter.Event[Any]] = Subject() frame = Frame(root, width=600, height=600) frame.bind("", mousemove.on_next) - text = 'TIME FLIES LIKE AN ARROW' + text = "TIME FLIES LIKE AN ARROW" - def on_next(info): + def on_next(info: Tuple[tkinter.Label, tkinter.Event[Any], int]): label, ev, i = info - label.place(x=ev.x + i*12 + 15, y=ev.y) + label.place(x=ev.x + i * 12 + 15, y=ev.y) - def handle_label(label, i): + def handle_label(label: tkinter.Label, i: int) -> Observable[Tuple[tkinter.Label, tkinter.Event[Any], int]]: label.config(dict(borderwidth=0, padx=0, pady=0)) mapper = ops.map(lambda ev: (label, ev, i)) - delayer = ops.delay(i*0.1) + delayer = ops.delay(i * 0.1) - return mousemove.pipe( - delayer, - mapper - ) + return mousemove.pipe(delayer, mapper) - labeler = ops.flat_map_indexed(handle_label) mapper = ops.map(lambda c: Label(frame, text=c)) + labeler = ops.flat_map_indexed(handle_label) - rx.from_(text).pipe( - mapper, - labeler - ).subscribe(on_next, on_error=print, scheduler=scheduler) + rx.from_(text).pipe(mapper, labeler).subscribe(on_next, on_error=print, scheduler=scheduler) frame.pack() root.mainloop() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/rx/__init__.py b/rx/__init__.py index 3dbac40db..9f97476a0 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1,16 +1,29 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin -from asyncio.futures import Future as _Future -from typing import Iterable, Callable, Any, Optional, Union, Mapping -from .core import Observable, pipe, typing +from typing import (TYPE_CHECKING, Any, Callable, Iterable, Mapping, Optional, + Tuple, TypeVar, Union) + +from .core import Observable, abc, pipe, typing from .internal.utils import alias +from .subject import Subject + +if TYPE_CHECKING: + # Futures cannot take generic argument before Python 3.9 + class _Future: + pass + +else: + from asyncio.futures import Future as _Future +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") # Please make sure the version here remains the same as in project.cfg -__version__ = '3.2.0' +__version__ = "3.2.0" -def amb(*sources: Observable) -> Observable: +def amb(*sources: Observable[_T]) -> Observable[_T]: """Propagates the observable sequence that emits first. .. marble:: @@ -34,13 +47,15 @@ def amb(*sources: Observable) -> Observable: """ from .core.observable.amb import _amb + return _amb(*sources) -def case(mapper: Callable[[], Any], - sources: Mapping, - default_source: Optional[Union[Observable, _Future]] = None - ) -> Observable: +def case( + mapper: Callable[[], _T1], + sources: Mapping[_T1, _T2], + default_source: Optional[Union[Observable[_T2], _Future]] = None, +) -> Observable[_T2]: """Uses mapper to determine which source in sources to use. .. marble:: @@ -70,10 +85,11 @@ def case(mapper: Callable[[], Any], """ from .core.observable.case import _case + return _case(mapper, sources, default_source) -def catch(*sources: Observable) -> Observable: +def catch(*sources: Observable[_T]) -> Observable[_T]: """Continues observable sequences which are terminated with an exception by switching over to the next observable sequence. @@ -97,10 +113,11 @@ def catch(*sources: Observable) -> Observable: """ from .core.observable.catch import _catch_with_iterable + return _catch_with_iterable(sources) -def catch_with_iterable(sources: Iterable[Observable]) -> Observable: +def catch_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: """Continues observable sequences that are terminated with an exception by switching over to the next observable sequence. @@ -126,10 +143,11 @@ def catch_with_iterable(sources: Iterable[Observable]) -> Observable: """ from .core.observable.catch import _catch_with_iterable + return _catch_with_iterable(sources) -def create(subscribe: typing.Subscription) -> Observable: +def create(subscribe: typing.Subscription[_T]) -> Observable[_T]: """Creates an observable sequence object from the specified subscription function. @@ -150,7 +168,7 @@ def create(subscribe: typing.Subscription) -> Observable: return Observable(subscribe) -def combine_latest(*sources: Observable) -> Observable: +def combine_latest(*sources: Observable[_T]) -> Observable[_T]: """Merges the specified observable sequences into one observable sequence by creating a tuple whenever any of the observable sequences emits an element. @@ -175,10 +193,11 @@ def combine_latest(*sources: Observable) -> Observable: """ from .core.observable.combinelatest import _combine_latest + return _combine_latest(*sources) -def concat(*sources: Observable) -> Observable: +def concat(*sources: Observable[_T]) -> Observable[_T]: """Concatenates all of the specified observable sequences. .. marble:: @@ -201,10 +220,11 @@ def concat(*sources: Observable) -> Observable: """ from .core.observable.concat import _concat_with_iterable + return _concat_with_iterable(sources) -def concat_with_iterable(sources: Iterable[Observable]) -> Observable: +def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: """Concatenates all of the specified observable sequences. .. marble:: @@ -229,11 +249,11 @@ def concat_with_iterable(sources: Iterable[Observable]) -> Observable: """ from .core.observable.concat import _concat_with_iterable + return _concat_with_iterable(sources) -def defer(factory: Callable[[typing.Scheduler], Union[Observable, _Future]] - ) -> Observable: +def defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], _Future]]) -> Observable[_T]: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -258,10 +278,11 @@ def defer(factory: Callable[[typing.Scheduler], Union[Observable, _Future]] """ from .core.observable.defer import _defer + return _defer(factory) -def empty(scheduler: Optional[typing.Scheduler] = None) -> Observable: +def empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: """Returns an empty observable sequence. .. marble:: @@ -283,10 +304,11 @@ def empty(scheduler: Optional[typing.Scheduler] = None) -> Observable: """ from .core.observable.empty import _empty + return _empty(scheduler) -def for_in(values: Iterable[Any], mapper: typing.Mapper) -> Observable: +def for_in(values: Iterable[_T1], mapper: typing.Mapper[_T1, _T2]) -> Observable[_T2]: """Concatenates the observable sequences obtained by running the specified result mapper for each element in the specified values. @@ -318,7 +340,7 @@ def for_in(values: Iterable[Any], mapper: typing.Mapper) -> Observable: return concat_with_iterable(map(mapper, values)) -def fork_join(*sources: Observable) -> Observable: +def fork_join(*sources: Observable[_T]) -> Observable[_T]: """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. @@ -344,12 +366,11 @@ def fork_join(*sources: Observable) -> Observable: """ from .core.observable.forkjoin import _fork_join + return _fork_join(*sources) -def from_callable(supplier: Callable[[], Any], - scheduler: Optional[typing.Scheduler] = None - ) -> Observable: +def from_callable(supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: """Returns an observable sequence that contains a single element generated by the given supplier, using the specified scheduler to send out observer messages. @@ -376,12 +397,11 @@ def from_callable(supplier: Callable[[], Any], """ from .core.observable.returnvalue import _from_callable + return _from_callable(supplier, scheduler) -def from_callback(func: Callable, - mapper: Optional[typing.Mapper] = None - ) -> Callable[[], Observable]: +def from_callback(func: Callable, mapper: Optional[typing.Mapper] = None) -> Callable[[], Observable]: """Converts a callback function to an observable sequence. Args: @@ -397,10 +417,11 @@ def from_callback(func: Callable, value of the arguments to the callback as a list. """ from .core.observable.fromcallback import _from_callback + return _from_callback(func, mapper) -def from_future(future: _Future) -> Observable: +def from_future(future: Union[_Future, Observable[_T]]) -> Observable[_T]: """Converts a Future to an Observable sequence .. marble:: @@ -419,10 +440,11 @@ def from_future(future: _Future) -> Observable: and failure. """ from .core.observable.fromfuture import _from_future + return _from_future(future) -def from_iterable(iterable: Iterable, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def from_iterable(iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: """Converts an iterable to an observable sequence. .. marble:: @@ -446,19 +468,21 @@ def from_iterable(iterable: Iterable, scheduler: Optional[typing.Scheduler] = No given iterable sequence. """ from .core.observable.fromiterable import from_iterable as from_iterable_ + return from_iterable_(iterable, scheduler) -from_ = alias('from_', 'Alias for :func:`rx.from_iterable`.', from_iterable) -from_list = alias('from_list', 'Alias for :func:`rx.from_iterable`.', from_iterable) +from_ = alias("from_", "Alias for :func:`rx.from_iterable`.", from_iterable) +from_list = alias("from_list", "Alias for :func:`rx.from_iterable`.", from_iterable) -def from_marbles(string: str, - timespan: typing.RelativeTime = 0.1, - scheduler: Optional[typing.Scheduler] = None, - lookup: Optional[Mapping] = None, - error: Optional[Exception] = None - ) -> Observable: +def from_marbles( + string: str, + timespan: typing.RelativeTime = 0.1, + scheduler: Optional[abc.SchedulerBase] = None, + lookup: Optional[Mapping] = None, + error: Optional[Exception] = None, +) -> Observable: """Convert a marble diagram string to a cold observable sequence, using an optional scheduler to enumerate the events. @@ -519,17 +543,19 @@ def from_marbles(string: str, """ from .core.observable.marbles import from_marbles as _from_marbles + return _from_marbles(string, timespan, lookup=lookup, error=error, scheduler=scheduler) -cold = alias('cold', 'Alias for :func:`rx.from_marbles`.', from_marbles) +cold = alias("cold", "Alias for :func:`rx.from_marbles`.", from_marbles) -def generate_with_relative_time(initial_state: Any, - condition: typing.Predicate, - iterate: typing.Mapper, - time_mapper: Callable[[Any], typing.RelativeTime] - ) -> Observable: +def generate_with_relative_time( + initial_state: Any, + condition: typing.Predicate, + iterate: typing.Mapper, + time_mapper: Callable[[Any], typing.RelativeTime], +) -> Observable: """Generates an observable sequence by iterating a state from an initial state until the condition fails. @@ -555,14 +581,13 @@ def generate_with_relative_time(initial_state: Any, Returns: The generated sequence. """ - from .core.observable.generatewithrelativetime import _generate_with_relative_time + from .core.observable.generatewithrelativetime import \ + _generate_with_relative_time + return _generate_with_relative_time(initial_state, condition, iterate, time_mapper) -def generate(initial_state: Any, - condition: typing.Predicate, - iterate: typing.Mapper - ) -> Observable: +def generate(initial_state: Any, condition: typing.Predicate, iterate: typing.Mapper) -> Observable: """Generates an observable sequence by running a state-driven loop producing the sequence's elements. @@ -585,16 +610,18 @@ def generate(initial_state: Any, The generated sequence. """ from .core.observable.generate import _generate + return _generate(initial_state, condition, iterate) -def hot(string: str, - timespan: typing.RelativeTime = 0.1, - duetime: typing.AbsoluteOrRelativeTime = 0.0, - scheduler: Optional[typing.Scheduler] = None, - lookup: Optional[Mapping] = None, - error: Optional[Exception] = None - ) -> Observable: +def hot( + string: str, + timespan: typing.RelativeTime = 0.1, + duetime: typing.AbsoluteOrRelativeTime = 0.0, + scheduler: Optional[abc.SchedulerBase] = None, + lookup: Optional[Mapping] = None, + error: Optional[Exception] = None, +) -> Observable: """Convert a marble diagram string to a hot observable sequence, using an optional scheduler to enumerate the events. @@ -657,13 +684,15 @@ def hot(string: str, """ from .core.observable.marbles import hot as _hot + return _hot(string, timespan, duetime, lookup=lookup, error=error, scheduler=scheduler) -def if_then(condition: Callable[[], bool], - then_source: Union[Observable, _Future], - else_source: Union[None, Observable, _Future] = None - ) -> Observable: +def if_then( + condition: Callable[[], bool], + then_source: Union[Observable, _Future], + else_source: Union[None, Observable, _Future] = None, +) -> Observable: """Determines whether an observable collection contains values. .. marble:: @@ -693,10 +722,11 @@ def if_then(condition: Callable[[], bool], else_source. """ from .core.observable.ifthen import _if_then + return _if_then(condition, then_source, else_source) -def interval(period: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def interval(period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: """Returns an observable sequence that produces a value after each period. .. marble:: @@ -720,6 +750,7 @@ def interval(period: typing.RelativeTime, scheduler: Optional[typing.Scheduler] An observable sequence that produces a value after each period. """ from .core.observable.interval import _interval + return _interval(period, scheduler) @@ -745,6 +776,7 @@ def merge(*sources: Observable) -> Observable: observable sequences. """ from .core.observable.merge import _merge + return _merge(*sources) @@ -762,6 +794,7 @@ def never() -> Observable: An observable sequence whose observers will never get called. """ from .core.observable.never import _never + return _never() @@ -817,14 +850,13 @@ def on_error_resume_next(*sources: Union[Observable, _Future]) -> Observable: even if a sequence terminates with an exception. """ from .core.observable.onerrorresumenext import _on_error_resume_next + return _on_error_resume_next(*sources) -def range(start: int, - stop: Optional[int] = None, - step: Optional[int] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Observable: +def range( + start: int, stop: Optional[int] = None, step: Optional[int] = None, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable: """Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to send out observer messages. @@ -852,10 +884,11 @@ def range(start: int, integral numbers. """ from .core.observable.range import _range + return _range(start, stop, step, scheduler) -def return_value(value: Any, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def return_value(value: Any, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: """Returns an observable sequence that contains a single element, using the specified scheduler to send out observer messages. There is an alias called 'just'. @@ -877,10 +910,11 @@ def return_value(value: Any, scheduler: Optional[typing.Scheduler] = None) -> Ob An observable sequence containing the single specified element. """ from .core.observable.returnvalue import _return_value + return _return_value(value, scheduler) -just = alias('just', 'Alias for :func:`rx.return_value`.', return_value) +just = alias("just", "Alias for :func:`rx.return_value`.", return_value) def repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Observable: @@ -907,10 +941,11 @@ def repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Obser specified number of times. """ from .core.observable.repeat import _repeat_value + return _repeat_value(value, repeat_count) -def start(func: Callable, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def start(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: """Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence. @@ -941,6 +976,7 @@ def start(func: Callable, scheduler: Optional[typing.Scheduler] = None) -> Obser or an exception. """ from .core.observable.start import _start + return _start(func, scheduler) @@ -963,10 +999,11 @@ def start_async(function_async: Callable[[], _Future]) -> Observable: or an exception. """ from .core.observable.startasync import _start_async + return _start_async(function_async) -def throw(exception: Exception, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def throw(exception: Exception, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: """Returns an observable sequence that terminates with an exception, using the specified scheduler to send out the single OnError message. @@ -990,11 +1027,15 @@ def throw(exception: Exception, scheduler: Optional[typing.Scheduler] = None) -> specified exception object. """ from .core.observable.throw import _throw + return _throw(exception, scheduler) -def timer(duetime: typing.AbsoluteOrRelativeTime, period: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None) -> Observable: +def timer( + duetime: typing.AbsoluteOrRelativeTime, + period: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Observable[int]: """Returns an observable sequence that produces a value after duetime has elapsed and then after each period. @@ -1026,10 +1067,11 @@ def timer(duetime: typing.AbsoluteOrRelativeTime, period: Optional[typing.Relati elapsed and then each period. """ from .core.observable.timer import _timer + return _timer(duetime, period, scheduler) -def to_async(func: Callable, scheduler: Optional[typing.Scheduler] = None) -> Callable: +def to_async(func: Callable[..., Any], scheduler: Optional[abc.SchedulerBase] = None) -> Callable: """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 @@ -1056,12 +1098,14 @@ def to_async(func: Callable, scheduler: Optional[typing.Scheduler] = None) -> Ca Asynchronous function. """ from .core.observable.toasync import _to_async + return _to_async(func, scheduler) -def using(resource_factory: Callable[[], typing.Disposable], - observable_factory: Callable[[typing.Disposable], Observable] - ) -> Observable: +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. @@ -1079,10 +1123,11 @@ def using(resource_factory: Callable[[], typing.Disposable], of the dependent resource object. """ from .core.observable.using import _using + return _using(resource_factory, observable_factory) -def with_latest_from(*sources: Observable) -> Observable: +def with_latest_from(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: """Merges the specified observable sequences into one observable sequence by creating a :class:`tuple` only when the first observable sequence produces an element. @@ -1107,10 +1152,11 @@ def with_latest_from(*sources: Observable) -> Observable: elements of the sources into a :class:`tuple`. """ from .core.observable.withlatestfrom import _with_latest_from + return _with_latest_from(*sources) -def zip(*args: Observable) -> Observable: +def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: """Merges the specified observable sequences into one observable sequence by creating a :class:`tuple` whenever all of the observable sequences have produced an element at a corresponding @@ -1135,4 +1181,8 @@ def zip(*args: Observable) -> Observable: elements of the sources as a :class:`tuple`. """ from .core.observable.zip import _zip + return _zip(*args) + + +__all__ = ["never", "pipe", "with_latest_from", "throw", "timer", "using", "to_async", "zip", "return_value", "Subject"] diff --git a/rx/core/__init__.py b/rx/core/__init__.py index b2632978f..e33c89f51 100644 --- a/rx/core/__init__.py +++ b/rx/core/__init__.py @@ -1,6 +1,6 @@ -# flake8: noqa +from . import abc +from .observable import ConnectableObservable, GroupedObservable, Observable +from .observer import Observer from .pipe import pipe -from .observable import Observable, ConnectableObservable -from .observable import GroupedObservable -from .observer import Observer +__all__ = ["abc", "pipe", "Observable", "ConnectableObservable", "GroupedObservable", "Observer"] diff --git a/rx/core/abc/__init__.py b/rx/core/abc/__init__.py index e55aef598..954d5d761 100644 --- a/rx/core/abc/__init__.py +++ b/rx/core/abc/__init__.py @@ -1,7 +1,22 @@ -from .disposable import Disposable -from .observable import Observable -from .observer import Observer -from .scheduler import Scheduler -from .periodicscheduler import PeriodicScheduler -from .startable import Startable -from .subject import Subject +from .disposable import DisposableBase +from .observable import ObservableBase, Subscription +from .observer import ObserverBase, OnCompleted, OnError, OnNext +from .periodicscheduler import PeriodicSchedulerBase +from .scheduler import ScheduledAction, SchedulerBase +from .startable import StartableBase +from .subject import SubjectBase + +__all__ = [ + "DisposableBase", + "ObserverBase", + "ObservableBase", + "OnCompleted", + "OnError", + "OnNext", + "SchedulerBase", + "PeriodicSchedulerBase", + "SubjectBase", + "Subscription", + "ScheduledAction", + "StartableBase", +] diff --git a/rx/core/abc/asyncobserver.py b/rx/core/abc/asyncobserver.py index 55069f888..ed78d08cc 100644 --- a/rx/core/abc/asyncobserver.py +++ b/rx/core/abc/asyncobserver.py @@ -1,4 +1,5 @@ from abc import abstractmethod + from .asyncobservable import AsyncObservable diff --git a/rx/core/abc/disposable.py b/rx/core/abc/disposable.py index b03ce1807..abc7be8f1 100644 --- a/rx/core/abc/disposable.py +++ b/rx/core/abc/disposable.py @@ -1,16 +1,29 @@ -from abc import ABC, abstractmethod +from abc import abstractmethod +from types import TracebackType +from typing import Optional, Protocol, Type, runtime_checkable -class Disposable(ABC): +@runtime_checkable +class DisposableBase(Protocol): """Disposable abstract base class. Untyped.""" + __slots__ = () + @abstractmethod - def dispose(self): + def dispose(self) -> None: + """Dispose the object: stop whatever we're doing and release all of the + resources we might be using. + """ raise NotImplementedError def __enter__(self): """Context management protocol.""" - def __exit__(self, typ, value, traceback): + def __exit__( + self, + exctype: Optional[Type[BaseException]], + excinst: Optional[BaseException], + exctb: Optional[TracebackType], + ): """Context management protocol.""" self.dispose() diff --git a/rx/core/abc/observable.py b/rx/core/abc/observable.py index d0bb005cd..6406d340e 100644 --- a/rx/core/abc/observable.py +++ b/rx/core/abc/observable.py @@ -1,11 +1,38 @@ from abc import ABC, abstractmethod +from typing import Callable, Generic, Optional, TypeVar +from .disposable import DisposableBase +from .observer import ObserverBase +from .scheduler import SchedulerBase -class Observable(ABC): - """Observable abstract base class. Untyped.""" +_T_out = TypeVar("_T_out", covariant=True) + + +class ObservableBase(Generic[_T_out], ABC): + """Observable abstract base class. + + Represents a push-style collection.""" __slots__ = () @abstractmethod - def subscribe(self, observer=None, *, scheduler=None): + def subscribe(self, observer: ObserverBase[_T_out], *, scheduler: Optional[SchedulerBase] = None) -> DisposableBase: + """Subscribe an observer to the observable sequence. + + Args: + observer: [Optional] The object that is to receive + notifications. + scheduler: [Optional] The default scheduler to use for this + subscription. + + Returns: + Disposable object representing an observer's subscription + to the observable sequence. + """ + raise NotImplementedError + + +Subscription = Callable[[ObserverBase[_T_out], Optional[SchedulerBase]], DisposableBase] + +__all__ = ["ObservableBase", "Subscription"] diff --git a/rx/core/abc/observer.py b/rx/core/abc/observer.py index a1ef22c63..31e484079 100644 --- a/rx/core/abc/observer.py +++ b/rx/core/abc/observer.py @@ -1,19 +1,48 @@ from abc import ABC, abstractmethod +from typing import Callable, Generic, TypeVar +_T = TypeVar("_T") +_T_in = TypeVar("_T_in", contravariant=True) -class Observer(ABC): - """Observer abstract base class. Untyped.""" +OnNext = Callable[[_T], None] +OnError = Callable[[Exception], None] +OnCompleted = Callable[[], None] + + +class ObserverBase(Generic[_T_in], ABC): + """Observer abstract base class + + An Observer is the entity that receives all emissions of a + subscribed Observable. + """ __slots__ = () @abstractmethod - def on_next(self, value): + def on_next(self, value: _T_in) -> None: + """Notifies the observer of a new element in the sequence. + + Args: + value: The received element. + """ + raise NotImplementedError @abstractmethod - def on_error(self, error): + def on_error(self, error: Exception) -> None: + """Notifies the observer that an exception has occurred. + + Args: + error: The error that has occurred. + """ + raise NotImplementedError @abstractmethod - def on_completed(self): + def on_completed(self) -> None: + """Notifies the observer of the end of the sequence.""" + raise NotImplementedError + + +__all__ = ["ObserverBase", "OnNext", "OnError", "OnCompleted"] diff --git a/rx/core/abc/periodicscheduler.py b/rx/core/abc/periodicscheduler.py index cd53a2724..9a34fa453 100644 --- a/rx/core/abc/periodicscheduler.py +++ b/rx/core/abc/periodicscheduler.py @@ -1,9 +1,39 @@ from abc import ABC, abstractmethod +from typing import Callable, Optional, TypeVar, Union +from .disposable import DisposableBase +from .scheduler import RelativeTime, ScheduledAction -class PeriodicScheduler(ABC): - """PeriodicScheduler abstract base class. Untyped.""" +_TState = TypeVar("_TState") # Can be anything + +ScheduledPeriodicAction = Callable[[Optional[_TState]], Optional[_TState]] +ScheduledSingleOrPeriodicAction = Union[ScheduledAction[_TState], ScheduledPeriodicAction[_TState]] + + +class PeriodicSchedulerBase(ABC): + """PeriodicScheduler abstract base class.""" + + __slots__ = () @abstractmethod - def schedule_periodic(self, period, action, state=None): + def schedule_periodic( + self, period: RelativeTime, action: ScheduledPeriodicAction[_TState], state: Optional[_TState] = None + ) -> DisposableBase: + """Schedules a periodic piece of work. + + Args: + period: Period in seconds or timedelta for running the + work periodically. + action: Action to be executed. + state: [Optional] Initial state passed to the action upon + the first iteration. + + Returns: + The disposable object used to cancel the scheduled + recurring action (best effort). + """ + return NotImplemented + + +__all__ = ["PeriodicSchedulerBase", "ScheduledPeriodicAction", "ScheduledSingleOrPeriodicAction", "RelativeTime"] diff --git a/rx/core/abc/scheduler.py b/rx/core/abc/scheduler.py index 530768855..b7c0f7cdd 100644 --- a/rx/core/abc/scheduler.py +++ b/rx/core/abc/scheduler.py @@ -1,37 +1,132 @@ from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Callable, Optional, TypeVar, Union +from .disposable import DisposableBase -class Scheduler(ABC): - """Scheduler abstract base class. Untyped.""" +_TState = TypeVar("_TState") # Can be anything + +AbsoluteTime = Union[datetime, float] +RelativeTime = Union[timedelta, float] +AbsoluteOrRelativeTime = Union[datetime, timedelta, float] +ScheduledAction = Callable[["SchedulerBase", Optional[_TState]], Optional[DisposableBase]] + + +class SchedulerBase(ABC): + """Scheduler abstract base class.""" + + __slots__ = () @property @abstractmethod - def now(self): + def now(self) -> datetime: + """Represents a notion of time for this scheduler. Tasks being + scheduled on a scheduler will adhere to the time denoted by this + property. + + Returns: + The scheduler's current time, as a datetime instance. + """ + return NotImplemented @abstractmethod - def schedule(self, action, state=None): + def schedule(self, action: ScheduledAction[_TState], state: Optional[_TState] = None) -> DisposableBase: + """Schedules an action to be executed. + + Args: + action: Action to be executed. + state: [Optional] state to be given to the action function. + + Returns: + The disposable object used to cancel the scheduled action + (best effort). + """ + return NotImplemented @abstractmethod - def schedule_relative(self, duetime, action, state=None): + def schedule_relative( + self, duetime: RelativeTime, action: ScheduledAction[_TState], state: Optional[_TState] = None + ) -> DisposableBase: + """Schedules an action to be executed after duetime. + + Args: + duetime: Relative time after which to execute the action. + action: Action to be executed. + state: [Optional] state to be given to the action function. + + Returns: + The disposable object used to cancel the scheduled action + (best effort). + """ + return NotImplemented @abstractmethod - def schedule_absolute(self, duetime, action, state=None): + def schedule_absolute( + self, duetime: AbsoluteTime, action: ScheduledAction[_TState], state: Optional[_TState] = None + ) -> DisposableBase: + """Schedules an action to be executed at duetime. + + Args: + duetime: Absolute time at which to execute the action. + action: Action to be executed. + state: [Optional] state to be given to the action function. + + Returns: + The disposable object used to cancel the scheduled action + (best effort). + """ + return NotImplemented @classmethod @abstractmethod - def to_seconds(cls, value): + def to_seconds(cls, value: AbsoluteOrRelativeTime) -> float: + """Converts time value to seconds. This method handles both absolute + (datetime) and relative (timedelta) values. If the argument is already + a float, it is simply returned unchanged. + + Args: + value: the time value to convert to seconds. + + Returns: + The value converted to seconds. + """ + return NotImplemented @classmethod @abstractmethod - def to_datetime(cls, value): + def to_datetime(cls, value: AbsoluteOrRelativeTime) -> datetime: + """Converts time value to datetime. This method handles both absolute + (float) and relative (timedelta) values. If the argument is already + a datetime, it is simply returned unchanged. + + Args: + value: the time value to convert to datetime. + + Returns: + The value converted to datetime. + """ + return NotImplemented @classmethod @abstractmethod - def to_timedelta(cls, value): + def to_timedelta(cls, value: AbsoluteOrRelativeTime) -> timedelta: + """Converts time value to timedelta. This method handles both absolute + (datetime) and relative (float) values. If the argument is already + a timedelta, it is simply returned unchanged. If the argument is an + absolute time, the result value will be the timedelta since the epoch, + January 1st, 1970, 00:00:00. + + Args: + value: the time value to convert to timedelta. + + Returns: + The value converted to timedelta. + """ + return NotImplemented diff --git a/rx/core/abc/startable.py b/rx/core/abc/startable.py index ddfca1bf0..b8668cad9 100644 --- a/rx/core/abc/startable.py +++ b/rx/core/abc/startable.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod -class Startable(ABC): +class StartableBase(ABC): """Abstract base class for Thread- and Process-like objects.""" + __slots__ = () @abstractmethod diff --git a/rx/core/abc/subject.py b/rx/core/abc/subject.py index 1986e88d8..d31b5ac05 100644 --- a/rx/core/abc/subject.py +++ b/rx/core/abc/subject.py @@ -1,25 +1,64 @@ from abc import abstractmethod +from typing import Optional, TypeVar -from .observable import Observable -from .observer import Observer +from .disposable import DisposableBase +from .observable import ObservableBase +from .observer import ObserverBase +from .scheduler import SchedulerBase +_T = TypeVar("_T") + + +class SubjectBase(ObserverBase[_T], ObservableBase[_T]): + """Subject abstract base class. + + Represents an object that is both an observable sequence as well + as an observer. + """ -class Subject(Observer, Observable): - """Subject abstract base class. Untyped.""" __slots__ = () @abstractmethod - def on_next(self, value): + def subscribe( + self, observer: Optional[ObserverBase[_T]] = None, *, scheduler: Optional[SchedulerBase] = None + ) -> DisposableBase: + """Subscribe an observer to the observable sequence. + + Args: + observer: [Optional] The object that is to receive + notifications. + scheduler: [Optional] The default scheduler to use for this + subscription. + + Returns: + Disposable object representing an observer's subscription + to the observable sequence. + """ + raise NotImplementedError @abstractmethod - def on_error(self, error): + def on_next(self, value: _T) -> None: + """Notifies the observer of a new element in the sequence. + + Args: + value: The received element. + """ + raise NotImplementedError @abstractmethod - def on_completed(self): + def on_error(self, error: Exception) -> None: + """Notifies the observer that an exception has occurred. + + Args: + error: The error that has occurred. + """ + raise NotImplementedError @abstractmethod - def subscribe(self, observer=None, *, scheduler=None): + def on_completed(self) -> None: + """Notifies the observer of the end of the sequence.""" + raise NotImplementedError diff --git a/rx/core/notification.py b/rx/core/notification.py index 0b13c7488..a31034ffb 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -1,21 +1,30 @@ from abc import abstractmethod +from typing import Any, Callable, Generic, Optional, TypeVar, Union + +from rx.core import abc, typing from rx.scheduler import ImmediateScheduler -from .. import typing -from .observer import Observer from .observable import Observable +from .observer import Observer + +_T = TypeVar("_T") -class Notification: +class Notification(Generic[_T]): """Represents a notification to an observer.""" def __init__(self) -> None: """Default constructor used by derived types.""" self.has_value = False self.value = None - self.kind = '' - - def accept(self, on_next, on_error=None, on_completed=None): + self.kind = "" + + def accept( + self, + on_next: Union[typing.OnNext[_T], abc.ObserverBase[_T]], + on_error: Optional[typing.OnError] = None, + on_completed: Optional[typing.OnCompleted] = None, + ) -> None: """Invokes the delegate corresponding to the notification or an observer and returns the produced result. @@ -33,20 +42,22 @@ def accept(self, on_next, on_error=None, on_completed=None): Returns: Result produced by the observation.""" - if isinstance(on_next, typing.Observer): + if isinstance(on_next, abc.ObserverBase): return self._accept_observer(on_next) return self._accept(on_next, on_error, on_completed) @abstractmethod - def _accept(self, on_next, on_error, on_completed): + def _accept( + self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + ) -> None: raise NotImplementedError @abstractmethod - def _accept_observer(self, observer): + def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None: raise NotImplementedError - def to_observable(self, scheduler=None): + def to_observable(self, scheduler: Optional[abc.SchedulerBase] = None) -> abc.ObservableBase[_T]: """Returns an observable sequence with a single notification, using the specified scheduler, else the immediate scheduler. @@ -59,43 +70,50 @@ def to_observable(self, scheduler=None): notification upon subscription. """ - scheduler = scheduler or ImmediateScheduler.singleton() + _scheduler = scheduler or ImmediateScheduler.singleton() - def subscribe(observer, scheduler=None): - def action(scheduler, state): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def action(scheduler: abc.SchedulerBase, state: Any): self._accept_observer(observer) - if self.kind == 'N': + if self.kind == "N": observer.on_completed() - return scheduler.schedule(action) + __scheduler = scheduler or _scheduler + return __scheduler.schedule(action) + return Observable(subscribe) - def equals(self, other): + def equals(self, other: "Notification[_T]") -> bool: """Indicates whether this instance and a specified object are equal.""" - other_string = '' if not other else str(other) + other_string = "" if not other else str(other) return str(self) == other_string - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return self.equals(other) -class OnNext(Notification): +class OnNext(Notification[_T]): """Represents an OnNext notification to an observer.""" - def __init__(self, value): + def __init__(self, value: _T): """Constructs a notification of a new value.""" super(OnNext, self).__init__() self.value = value self.has_value = True - self.kind = 'N' - - def _accept(self, on_next, on_error=None, on_completed=None): + self.kind = "N" + + def _accept( + self, + on_next: typing.OnNext[_T], + on_error: Optional[typing.OnError] = None, + on_completed: Optional[typing.OnCompleted] = None, + ) -> None: return on_next(self.value) - def _accept_observer(self, observer): + def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None: return observer.on_next(self.value) def __str__(self): @@ -105,46 +123,50 @@ def __str__(self): return "OnNext(%s)" % str(val) -class OnError(Notification): +class OnError(Notification[_T]): """Represents an OnError notification to an observer.""" - def __init__(self, exception): + def __init__(self, exception: Exception): """Constructs a notification of an exception.""" super(OnError, self).__init__() self.exception = exception - self.kind = 'E' + self.kind = "E" - def _accept(self, on_next, on_error, on_completed): - return on_error(self.exception) + def _accept( + self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + ) -> None: + return on_error(self.exception) if on_error else None - def _accept_observer(self, observer): + def _accept_observer(self, observer: abc.ObserverBase[_T]): return observer.on_error(self.exception) def __str__(self): return "OnError(%s)" % str(self.exception) -class OnCompleted(Notification): +class OnCompleted(Notification[_T]): """Represents an OnCompleted notification to an observer.""" def __init__(self): """Constructs a notification of the end of a sequence.""" super(OnCompleted, self).__init__() - self.kind = 'C' + self.kind = "C" - def _accept(self, on_next, on_error, on_completed): - return on_completed() + def _accept( + self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + ) -> None: + return on_completed() if on_completed else None - def _accept_observer(self, observer): + def _accept_observer(self, observer: abc.ObserverBase[_T]): return observer.on_completed() def __str__(self): return "OnCompleted()" -def from_notifier(handler): +def from_notifier(handler: Callable[[Notification[_T]], None]) -> Observer[_T]: """Creates an observer from a notification callback. Args: @@ -155,10 +177,10 @@ def from_notifier(handler): a notification corresponding to each message it receives. """ - def _on_next(value): + def _on_next(value: _T) -> None: return handler(OnNext(value)) - def _on_error(error): + def _on_error(error: Exception): return handler(OnError(error)) def _on_completed(): diff --git a/rx/core/observable/__init__.py b/rx/core/observable/__init__.py index afeb88ba3..a48725738 100644 --- a/rx/core/observable/__init__.py +++ b/rx/core/observable/__init__.py @@ -1,3 +1,5 @@ -from .observable import Observable from .connectableobservable import ConnectableObservable from .groupedobservable import GroupedObservable +from .observable import Observable + +__all__ = ["Observable", "ConnectableObservable", "GroupedObservable"] diff --git a/rx/core/observable/amb.py b/rx/core/observable/amb.py index bcea4b613..7932be675 100644 --- a/rx/core/observable/amb.py +++ b/rx/core/observable/amb.py @@ -1,10 +1,13 @@ -from rx import never +from typing import TypeVar +from rx import never from rx import operators as _ from rx.core import Observable +_T = TypeVar("_T") + -def _amb(*sources: Observable) -> Observable: +def _amb(*sources: Observable[_T]) -> Observable[_T]: """Propagates the observable sequence that reacts first. Example: @@ -15,12 +18,15 @@ def _amb(*sources: Observable) -> Observable: whichever reacted first. """ - acc = never() + acc: Observable[_T] = never() - def func(previous, current): + def func(previous: Observable[_T], current: Observable[_T]): return _.amb(previous)(current) for source in sources: acc = func(acc, source) return acc + + +__all__ = ["_amb"] diff --git a/rx/core/observable/case.py b/rx/core/observable/case.py index f98ae2f41..1a4aa7907 100644 --- a/rx/core/observable/case.py +++ b/rx/core/observable/case.py @@ -1,25 +1,33 @@ -from typing import Optional, Union, Mapping, Callable, Any from asyncio import Future +from typing import Any, Callable, Mapping, Optional, TypeVar, Union -from rx import empty, defer, from_future +from rx import defer, empty, from_future from rx.core import Observable from rx.internal.utils import is_future +_Key = TypeVar("_Key") +_T = TypeVar("_T") -def _case(mapper: Callable[[], Any], - sources: Mapping, - default_source: Optional[Union[Observable, Future]] = None - ) -> Observable: - default_source = default_source or empty() +def _case( + mapper: Callable[[], _Key], + sources: Mapping[_Key, Observable[_T]], + default_source: Optional[Union[Observable[_T], Future]] = None, +) -> Observable[_T]: - def factory(_) -> Observable: + default_source: Union[Observable[_T], Future] = default_source or empty() + + def factory(_) -> Observable[_T]: try: result = sources[mapper()] except KeyError: result = default_source - result = from_future(result) if is_future(result) else result + result: Observable[_T] = from_future(result) if is_future(result) else result return result + return defer(factory) + + +__all__ = ["_case"] diff --git a/rx/core/observable/catch.py b/rx/core/observable/catch.py index 49f993180..4908217d9 100644 --- a/rx/core/observable/catch.py +++ b/rx/core/observable/catch.py @@ -1,12 +1,14 @@ -from typing import Iterable +from typing import Any, Iterable, Optional, TypeVar -from rx.disposable import Disposable -from rx.core import Observable -from rx.disposable import SingleAssignmentDisposable, CompositeDisposable, SerialDisposable +from rx.core import Observable, abc +from rx.disposable import (CompositeDisposable, Disposable, SerialDisposable, + SingleAssignmentDisposable) from rx.scheduler import CurrentThreadScheduler +_T = TypeVar("_T") -def _catch_with_iterable(sources: Iterable[Observable]) -> Observable: + +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. @@ -26,7 +28,7 @@ def _catch_with_iterable(sources: Iterable[Observable]) -> Observable: sources_ = iter(sources) - def subscribe(observer, scheduler_=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler_ or CurrentThreadScheduler.singleton() subscription = SerialDisposable() @@ -34,8 +36,8 @@ def subscribe(observer, scheduler_=None): last_exception = None is_disposed = False - def action(action1, state=None): - def on_error(exn): + def action(action1, state: Any = None): + def on_error(exn: Exception) -> None: nonlocal last_exception last_exception = exn cancelable.disposable = _scheduler.schedule(action) @@ -62,5 +64,7 @@ def on_error(exn): def dispose(): nonlocal is_disposed is_disposed = True + return CompositeDisposable(subscription, cancelable, Disposable(dispose)) + return Observable(subscribe) diff --git a/rx/core/observable/combinelatest.py b/rx/core/observable/combinelatest.py index dc03974e0..54aaaf2a6 100644 --- a/rx/core/observable/combinelatest.py +++ b/rx/core/observable/combinelatest.py @@ -1,10 +1,10 @@ -from typing import Optional +from typing import Any, List, Optional, Tuple -from rx.core import Observable, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -def _combine_latest(*sources: Observable) -> Observable: +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. @@ -19,9 +19,9 @@ def _combine_latest(*sources: Observable) -> Observable: parent = sources[0] - def subscribe(observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> CompositeDisposable: + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> CompositeDisposable: n = len(sources) has_value = [False] * n @@ -29,7 +29,7 @@ def subscribe(observer: typing.Observer, is_done = [False] * n values = [None] * n - def _next(i): + def _next(i: Any): has_value[i] = True if has_value_all[0] or all(has_value): @@ -41,17 +41,17 @@ def _next(i): has_value_all[0] = all(has_value) - def done(i): + def done(i: Any): is_done[i] = True if all(is_done): observer.on_completed() - subscriptions = [None] * n + subscriptions: List[Optional[SingleAssignmentDisposable]] = [None] * n - def func(i): + def func(i: int): subscriptions[i] = SingleAssignmentDisposable() - def on_next(x): + def on_next(x: Any) -> None: with parent.lock: values[i] = x _next(i) @@ -60,9 +60,12 @@ def on_completed(): with parent.lock: done(i) - subscriptions[i].disposable = sources[i].subscribe_(on_next, observer.on_error, on_completed, scheduler) + subscription = subscriptions[i] + assert subscription + subscription.disposable = sources[i].subscribe_(on_next, observer.on_error, on_completed, scheduler) for idx in range(n): func(idx) return CompositeDisposable(subscriptions) + return Observable(subscribe) diff --git a/rx/core/observable/concat.py b/rx/core/observable/concat.py index 72bc5312d..e0e893b9c 100644 --- a/rx/core/observable/concat.py +++ b/rx/core/observable/concat.py @@ -1,14 +1,15 @@ -from typing import Iterable +from typing import Any, Iterable, Optional, TypeVar -from rx.disposable import Disposable -from rx.core import Observable -from rx.disposable import SingleAssignmentDisposable, CompositeDisposable, SerialDisposable +from rx.core import Observable, abc +from rx.disposable import (CompositeDisposable, Disposable, SerialDisposable, + SingleAssignmentDisposable) from rx.scheduler import CurrentThreadScheduler +_T = TypeVar("_T") -def _concat_with_iterable(sources: Iterable[Observable]) -> Observable: - def subscribe(observer, scheduler_=None): +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) @@ -17,7 +18,7 @@ def subscribe(observer, scheduler_=None): cancelable = SerialDisposable() is_disposed = False - def action(action1, state=None): + def action(scheduler: abc.SchedulerBase, state: Any = None): nonlocal is_disposed if is_disposed: return @@ -43,4 +44,8 @@ def dispose(): is_disposed = True return CompositeDisposable(subscription, cancelable, Disposable(dispose)) + return Observable(subscribe) + + +__all__ = ["_concat_with_iterable"] diff --git a/rx/core/observable/connectableobservable.py b/rx/core/observable/connectableobservable.py index ab0acd514..09adc08f1 100644 --- a/rx/core/observable/connectableobservable.py +++ b/rx/core/observable/connectableobservable.py @@ -1,14 +1,18 @@ -from rx.disposable import Disposable -from rx.disposable import CompositeDisposable +from typing import List, Optional, TypeVar + +from rx.core import abc +from rx.disposable import CompositeDisposable, Disposable from .observable import Observable +_T = TypeVar("_T") + -class ConnectableObservable(Observable): +class ConnectableObservable(Observable[_T]): """Represents an observable that can be connected and disconnected.""" - def __init__(self, source, subject): + def __init__(self, source: abc.ObservableBase[_T], subject: abc.SubjectBase[_T]): self.subject = subject self.has_subscription = False self.subscription = None @@ -16,10 +20,10 @@ def __init__(self, source, subject): super().__init__() - def _subscribe_core(self, observer, scheduler=None): + def _subscribe_core(self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): return self.subject.subscribe(observer, scheduler=scheduler) - def connect(self, scheduler=None): + def connect(self, scheduler: Optional[abc.SchedulerBase] = None): """Connects the observable.""" if not self.has_subscription: @@ -33,7 +37,7 @@ def dispose(): return self.subscription - def auto_connect(self, subscriber_count=1): + 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 @@ -42,7 +46,7 @@ def auto_connect(self, subscriber_count=1): subscribers. """ - connectable_subscription = [None] + connectable_subscription: List[Optional[abc.DisposableBase]] = [None] count = [0] source = self is_connected = [False] @@ -51,7 +55,7 @@ def auto_connect(self, subscriber_count=1): connectable_subscription[0] = source.connect() is_connected[0] = True - def subscribe(observer, scheduler=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): count[0] += 1 should_connect = count[0] == subscriber_count and not is_connected[0] subscription = source.subscribe(observer) @@ -65,4 +69,5 @@ def dispose(): is_connected[0] = False return Disposable(dispose) + return Observable(subscribe) diff --git a/rx/core/observable/defer.py b/rx/core/observable/defer.py index bb2833287..4cf4206f0 100644 --- a/rx/core/observable/defer.py +++ b/rx/core/observable/defer.py @@ -1,14 +1,12 @@ -from typing import Callable, Union from asyncio import Future +from typing import Callable, Union -from rx import throw, from_future -from rx.core import Observable -from rx.core.typing import Scheduler +from rx import from_future, throw +from rx.core import Observable, abc from rx.internal.utils import is_future -def _defer(factory: Callable[[Scheduler], Union[Observable, Future]] - ) -> Observable: +def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable, Future]]) -> Observable: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -32,4 +30,5 @@ def subscribe(observer, scheduler=None): result = from_future(result) if is_future(result) else result return result.subscribe(observer, scheduler=scheduler) + return Observable(subscribe) diff --git a/rx/core/observable/empty.py b/rx/core/observable/empty.py index c33c69eb2..6068cc0de 100644 --- a/rx/core/observable/empty.py +++ b/rx/core/observable/empty.py @@ -1,18 +1,17 @@ from typing import Any, Optional -from rx.core import typing, Observable +from rx.core import Observable, abc from rx.scheduler import ImmediateScheduler -def _empty(scheduler: Optional[typing.Scheduler] = None) -> Observable: - def subscribe(observer: typing.Observer, - scheduler_: Optional[typing.Scheduler] = None - ) -> typing.Disposable: +def _empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable: + def subscribe(observer: abc.ObserverBase, scheduler_: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or ImmediateScheduler.singleton() - def action(_: typing.Scheduler, __: Any) -> None: + def action(_: abc.SchedulerBase, __: Any) -> None: observer.on_completed() return _scheduler.schedule(action) + return Observable(subscribe) diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index 2b3243759..6946e3378 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -1,6 +1,6 @@ from typing import Optional -from rx.core import Observable, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable @@ -19,9 +19,7 @@ def _fork_join(*sources: Observable) -> Observable: parent = sources[0] - def subscribe(observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> CompositeDisposable: + def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> CompositeDisposable: n = len(sources) values = [None] * n is_done = [False] * n @@ -55,12 +53,7 @@ def on_completed(): with parent.lock: done(i) - subscriptions[i].disposable = sources[i].subscribe_( - on_next, - observer.on_error, - on_completed, - scheduler - ) + subscriptions[i].disposable = sources[i].subscribe_(on_next, observer.on_error, on_completed, scheduler) for i in range(n): _subscribe(i) diff --git a/rx/core/observable/fromcallback.py b/rx/core/observable/fromcallback.py index 5d6d88b23..385e1b959 100644 --- a/rx/core/observable/fromcallback.py +++ b/rx/core/observable/fromcallback.py @@ -1,9 +1,8 @@ from typing import Callable, Optional -from rx.disposable import Disposable -from rx.core import typing -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Mapper +from rx.disposable import Disposable def _from_callback(func: Callable, mapper: Optional[Mapper] = None) -> Callable[[], Observable]: @@ -24,13 +23,13 @@ def _from_callback(func: Callable, mapper: Optional[Mapper] = None) -> Callable[ def function(*args): arguments = list(args) - def subscribe(observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: def handler(*args): results = list(args) if mapper: try: results = mapper(args) - except Exception as err: # pylint: disable=broad-except + except Exception as err: # pylint: disable=broad-except observer.on_error(err) return @@ -48,4 +47,5 @@ def handler(*args): return Disposable() return Observable(subscribe) + return function diff --git a/rx/core/observable/fromfuture.py b/rx/core/observable/fromfuture.py index 70ab76700..a41c87a2a 100644 --- a/rx/core/observable/fromfuture.py +++ b/rx/core/observable/fromfuture.py @@ -1,13 +1,11 @@ import asyncio -from asyncio.futures import Future -from typing import Optional +from typing import Any, Optional +from rx.core import Observable, abc, typing from rx.disposable import Disposable -from rx.core import typing -from rx.core import Observable -def _from_future(future: Future) -> Observable: +def _from_future(future: typing.Future) -> Observable[Any]: """Converts a Future to an Observable sequence Args: @@ -20,13 +18,10 @@ def _from_future(future: Future) -> Observable: and failure. """ - def subscribe(observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: - - def done(future): + def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + def done(future: typing.Future): try: - value = future.result() + value: Any = future.result() except (Exception, asyncio.CancelledError) as ex: # pylint: disable=broad-except observer.on_error(ex) else: @@ -42,3 +37,6 @@ def dispose() -> None: return Disposable(dispose) return Observable(subscribe) + + +__all__ = ["_from_future"] diff --git a/rx/core/observable/fromiterable.py b/rx/core/observable/fromiterable.py index a28462190..f8ac42bf0 100644 --- a/rx/core/observable/fromiterable.py +++ b/rx/core/observable/fromiterable.py @@ -1,11 +1,13 @@ -from typing import Iterable, Any, Optional +from typing import Any, Iterable, Optional, TypeVar -from rx.core import Observable, typing -from rx.scheduler import CurrentThreadScheduler +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, Disposable +from rx.scheduler import CurrentThreadScheduler +_T = TypeVar("_T") -def from_iterable(iterable: Iterable, scheduler: Optional[typing.Scheduler] = None) -> Observable: + +def from_iterable(iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: """Converts an iterable to an observable sequence. Example: @@ -20,12 +22,12 @@ def from_iterable(iterable: Iterable, scheduler: Optional[typing.Scheduler] = No given iterable sequence. """ - def subscribe(observer: typing.Observer, scheduler_: Optional[typing.Scheduler] = None) -> typing.Disposable: + 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(_: typing.Scheduler, __: Any = None) -> None: + def action(_: abc.SchedulerBase, __: Any = None) -> None: nonlocal disposed try: @@ -43,4 +45,5 @@ def dispose() -> None: disp = Disposable(dispose) return CompositeDisposable(_scheduler.schedule(action), disp) + return Observable(subscribe) diff --git a/rx/core/observable/generate.py b/rx/core/observable/generate.py index a42413429..9bcb3c224 100644 --- a/rx/core/observable/generate.py +++ b/rx/core/observable/generate.py @@ -2,8 +2,8 @@ from rx.core import Observable from rx.core.typing import Mapper, Predicate -from rx.scheduler import CurrentThreadScheduler from rx.disposable import MultipleAssignmentDisposable +from rx.scheduler import CurrentThreadScheduler def _generate(initial_state: Any, diff --git a/rx/core/observable/generatewithrelativetime.py b/rx/core/observable/generatewithrelativetime.py index 9f544eedf..a0d676b51 100644 --- a/rx/core/observable/generatewithrelativetime.py +++ b/rx/core/observable/generatewithrelativetime.py @@ -1,9 +1,9 @@ from typing import Any, Callable from rx.core import Observable -from rx.core.typing import Predicate, Mapper, RelativeTime -from rx.scheduler import TimeoutScheduler +from rx.core.typing import Mapper, Predicate, RelativeTime from rx.disposable import MultipleAssignmentDisposable +from rx.scheduler import TimeoutScheduler def _generate_with_relative_time(initial_state: Any, diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index 0db79c251..10e183a3c 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -1,9 +1,8 @@ -from typing import Callable, Union, cast from asyncio import Future +from typing import Callable, Union, cast import rx -from rx.core import Observable -from rx.core.typing import Scheduler +from rx.core import Observable, abc from rx.internal.utils import is_future @@ -37,7 +36,7 @@ def _if_then(condition: Callable[[], bool], then_source = rx.from_future(cast(Future, then_source)) if is_future(then_source) else then_source else_source = rx.from_future(cast(Future, else_source)) if is_future(else_source) else else_source - def factory(_: Scheduler): + def factory(_: abc.SchedulerBase): return then_source if condition() else else_source return rx.defer(factory) diff --git a/rx/core/observable/interval.py b/rx/core/observable/interval.py index 19f4e9d8d..0a2576caa 100644 --- a/rx/core/observable/interval.py +++ b/rx/core/observable/interval.py @@ -1,11 +1,12 @@ from typing import Optional from rx import timer -from rx.core import Observable, typing +from rx.core import Observable, abc, typing -def _interval(period: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Observable: +def _interval(period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None) -> Observable[int]: return timer(period, period, scheduler) + + +__all__ = ["_interval"] diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index a11f6461c..5522b9cc0 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -1,14 +1,14 @@ -from typing import List, Tuple, Optional, Mapping import re import threading from datetime import datetime, timedelta +from typing import List, Mapping, Optional, Tuple from rx import Observable from rx.core import notification +from rx.core.abc import SchedulerBase +from rx.core.typing import AbsoluteOrRelativeTime, RelativeTime from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import NewThreadScheduler -from rx.core.typing import RelativeTime, AbsoluteOrRelativeTime, Scheduler - new_thread_scheduler = NewThreadScheduler() @@ -22,22 +22,25 @@ # 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, - ]) +pattern = r"|".join( + [ + pattern_group, + pattern_ticks, + pattern_comma_error, + pattern_element, + ] +) tokens = re.compile(pattern) -def hot(string: str, - timespan: RelativeTime = 0.1, - duetime: AbsoluteOrRelativeTime = 0.0, - lookup: Optional[Mapping] = None, - error: Optional[Exception] = None, - scheduler: Optional[Scheduler] = None - ) -> Observable: +def hot( + string: str, + timespan: RelativeTime = 0.1, + duetime: AbsoluteOrRelativeTime = 0.0, + lookup: Optional[Mapping] = None, + error: Optional[Exception] = None, + scheduler: Optional[SchedulerBase] = None, +) -> Observable: _scheduler = scheduler or new_thread_scheduler @@ -51,7 +54,7 @@ def hot(string: str, lookup=lookup, error=error, raise_stopped=True, - ) + ) lock = threading.RLock() is_stopped = False @@ -81,7 +84,7 @@ def action(scheduler, state=None): for observer in observers: notification.accept(observer) - if notification.kind in ('C', 'E'): + if notification.kind in ("C", "E"): is_stopped = True return action @@ -96,12 +99,13 @@ def action(scheduler, state=None): return Observable(subscribe) -def from_marbles(string: str, - timespan: RelativeTime = 0.1, - lookup: Optional[Mapping] = None, - error: Optional[Exception] = None, - scheduler: Optional[Scheduler] = None - ) -> Observable: +def from_marbles( + string: str, + timespan: RelativeTime = 0.1, + lookup: Optional[Mapping] = None, + error: Optional[Exception] = None, + scheduler: Optional[SchedulerBase] = None, +) -> Observable: messages = parse(string, timespan=timespan, lookup=lookup, error=error, raise_stopped=True) @@ -122,16 +126,18 @@ def action(*_, **__): schedule_msg(message) return disp + return Observable(subscribe) -def parse(string: str, - timespan: RelativeTime = 1.0, - time_shift: RelativeTime = 0.0, - lookup: Optional[Mapping] = None, - error: Optional[Exception] = None, - raise_stopped: bool = False - ) -> List[Tuple[RelativeTime, notification.Notification]]: +def parse( + string: str, + timespan: RelativeTime = 1.0, + time_shift: RelativeTime = 0.0, + lookup: Optional[Mapping] = None, + error: Optional[Exception] = None, + raise_stopped: bool = False, +) -> List[Tuple[RelativeTime, notification.Notification]]: """Convert a marble diagram string to a list of messages. Each character in the string will advance time by timespan @@ -189,7 +195,7 @@ def parse(string: str, """ - error = error or Exception('error') + error = error or Exception("error") lookup = lookup or {} if isinstance(timespan, timedelta): @@ -197,7 +203,7 @@ def parse(string: str, if isinstance(time_shift, timedelta): time_shift = time_shift.total_seconds() - string = string.replace(' ', '') + string = string.replace(" ", "") # try to cast a string to an int, then to a float def try_number(element): @@ -210,9 +216,9 @@ def try_number(element): return element def map_element(time, element): - if element == '|': + if element == "|": return (time, notification.OnCompleted()) - elif element == '#': + elif element == "#": return (time, notification.OnError(error)) else: value = try_number(element) @@ -225,9 +231,9 @@ def check_stopped(element): nonlocal is_stopped if raise_stopped: if is_stopped: - raise ValueError('Elements cannot be declared after a # or | symbol.') + raise ValueError("Elements cannot be declared after a # or | symbol.") - if element in ('#', '|'): + if element in ("#", "|"): is_stopped = True iframe = 0 @@ -238,10 +244,10 @@ def check_stopped(element): group, ticks, comma_error, element = results if group: - elements = group[1:-1].split(',') + elements = group[1:-1].split(",") for elm in elements: check_stopped(elm) - grp_messages = [map_element(timestamp, elm) for elm in elements if elm !=''] + grp_messages = [map_element(timestamp, elm) for elm in elements if elm != ""] messages.extend(grp_messages) iframe += len(group) diff --git a/rx/core/observable/never.py b/rx/core/observable/never.py index 8b7f2c7b4..a802b26f4 100644 --- a/rx/core/observable/never.py +++ b/rx/core/observable/never.py @@ -1,10 +1,10 @@ -from typing import Optional +from typing import Any, Optional +from rx.core import Observable, abc from rx.disposable import Disposable -from rx.core import Observable, typing -def _never() -> Observable: +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). @@ -12,7 +12,10 @@ def _never() -> Observable: An observable sequence whose observers will never get called. """ - def subscribe(observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: return Disposable() return Observable(subscribe) + + +__all__ = ["_never"] diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index f3f0be898..18f9f27ac 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -1,31 +1,35 @@ # By design, pylint: disable=C0302 -import threading +from __future__ import annotations + import asyncio -from typing import Any, Callable, Optional, Union, TypeVar, cast, overload +import threading +from typing import Any, Callable, Optional, TypeVar, Union, cast, overload +from rx.core import abc from rx.disposable import Disposable from rx.scheduler import CurrentThreadScheduler from rx.scheduler.eventloop import AsyncIOScheduler from ..observer import AutoDetachObserver -from .. import typing, abc -A = TypeVar('A') -B = TypeVar('B') -C = TypeVar('C') -D = TypeVar('D') -E = TypeVar('E') -F = TypeVar('F') -G = TypeVar('G') +_A = TypeVar("_A") +_B = TypeVar("_B") +_C = TypeVar("_C") +_D = TypeVar("_D") +_E = TypeVar("_E") +_F = TypeVar("_F") +_G = TypeVar("_G") + +_T = TypeVar("_T") -class Observable(typing.Observable): +class Observable(abc.ObservableBase[_T]): """Observable base class. Represents a push-style collection, which you can :func:`pipe ` into :mod:`operators `.""" - def __init__(self, subscribe: Optional[typing.Subscription] = None) -> None: + def __init__(self, subscribe: Optional[abc.Subscription[_T]] = None) -> None: """Creates an observable sequence object from the specified subscription function. @@ -37,24 +41,42 @@ def __init__(self, subscribe: Optional[typing.Subscription] = None) -> None: self.lock = threading.RLock() self._subscribe = subscribe - def _subscribe_core(self, - observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: + def _subscribe_core( + self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: return self._subscribe(observer, scheduler) if self._subscribe else Disposable() - def subscribe(self, # pylint: disable=too-many-arguments,arguments-differ - observer: Optional[Union[typing.Observer, typing.OnNext]] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None, - on_next: Optional[typing.OnNext] = None, - *, - scheduler: Optional[typing.Scheduler] = None, - ) -> typing.Disposable: + @overload + def subscribe( + self, + observer: abc.ObserverBase[_T], + *, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + ... + + @overload + def subscribe( + self, + observer: Optional[abc.OnNext[_T]] = None, + *, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + ... + + def subscribe( + self, + observer: Union[abc.ObserverBase[_T], abc.OnNext[_T], None] = None, + on_error: Optional[abc.OnError] = None, + on_completed: Optional[abc.OnCompleted] = None, + on_next: Optional[abc.OnNext[_T]] = 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 <..typing.Observer>` or if + 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. @@ -85,22 +107,25 @@ def subscribe(self, # pylint: disable=too-many-arguments,arguments-differ the observable sequence. """ if observer: - if isinstance(observer, typing.Observer) \ - or hasattr(observer, 'on_next') \ - and callable(getattr(observer, 'on_next')): - on_next = cast(typing.Observer, observer).on_next - on_error = cast(typing.Observer, observer).on_error - on_completed = cast(typing.Observer, observer).on_completed + if ( + isinstance(observer, abc.ObserverBase) + or hasattr(observer, "on_next") + and callable(getattr(observer, "on_next")) + ): + on_next = cast(abc.ObserverBase[_T], observer).on_next + on_error = cast(abc.ObserverBase[_T], observer).on_error + on_completed = cast(abc.ObserverBase[_T], observer).on_completed else: on_next = observer - return self.subscribe_(on_next, on_error, on_completed, scheduler) - - def subscribe_(self, - on_next: Optional[typing.OnNext] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: + return self.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) + + def subscribe_( + self, + on_next: Optional[abc.OnNext[_T]] = None, + on_error: Optional[abc.OnError] = None, + on_completed: Optional[abc.OnCompleted] = None, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: """Subscribe callbacks to the observable sequence. Examples: @@ -122,17 +147,18 @@ def subscribe_(self, the observable sequence. """ - auto_detach_observer = AutoDetachObserver(on_next, on_error, on_completed) + auto_detach_observer: AutoDetachObserver[_T] = AutoDetachObserver(on_next, on_error, on_completed) - def fix_subscriber(subscriber): + 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 not hasattr(subscriber, 'dispose'): - subscriber = Disposable(subscriber) - return subscriber + if isinstance(subscriber, abc.DisposableBase) or hasattr(subscriber, "dispose"): + return subscriber - def set_disposable(_: abc.Scheduler = None, __: Any = None): + return Disposable(subscriber) + + def set_disposable(_: Optional[abc.SchedulerBase] = None, __: Any = None): try: subscriber = self._subscribe_core(auto_detach_observer, scheduler) except Exception as ex: # By design. pylint: disable=W0703 @@ -159,9 +185,9 @@ def set_disposable(_: abc.Scheduler = None, __: Any = None): return Disposable(auto_detach_observer.dispose) @overload - def pipe(self, - *operators: Callable[['Observable'], 'Observable'] - ) -> 'Observable': # pylint: disable=no-self-use + def pipe( + self, *operators: Callable[[Observable[_T]], Observable[_A]] + ) -> Observable[_A]: # pylint: disable=no-self-use """Compose multiple operators left to right. Composes zero or more operators into a functional composition. @@ -183,66 +209,78 @@ def pipe(self, ... @overload - def pipe(self) -> 'Observable': # pylint: disable=function-redefined, no-self-use - ... # pylint: disable=pointless-statement + def pipe(self) -> Observable[_T]: + ... @overload - def pipe(self, op1: Callable[['Observable'], A]) -> A: # pylint: disable=function-redefined, no-self-use - ... # pylint: disable=pointless-statement + def pipe(self, __op1: Callable[[Observable[_T]], _A]) -> _A: + ... @overload - def pipe(self, # pylint: disable=function-redefined, no-self-use - op1: Callable[['Observable'], A], - op2: Callable[[A], B]) -> B: - ... # pylint: disable=pointless-statement + def pipe( + self, + __op1: Callable[[Observable[_T]], _A], + __op2: Callable[[_A], _B], + ) -> _B: + ... @overload - def pipe(self, # pylint: disable=function-redefined, no-self-use - op1: Callable[['Observable'], A], - op2: Callable[[A], B], - op3: Callable[[B], C]) -> C: - ... # pylint: disable=pointless-statement + def pipe( + self, + op1: Callable[[Observable[_T]], _A], + op2: Callable[[_A], _B], + op3: Callable[[_B], _C], + ) -> _C: + ... @overload - def pipe(self, # pylint: disable=function-redefined, no-self-use - op1: Callable[['Observable'], A], - op2: Callable[[A], B], - op3: Callable[[B], C], - op4: Callable[[C], D]) -> D: - ... # pylint: disable=pointless-statement + def pipe( + self, + op1: Callable[[Observable[_T]], _A], + op2: Callable[[_A], _B], + op3: Callable[[_B], _C], + op4: Callable[[_C], _D], + ) -> _D: + ... @overload - def pipe(self, # pylint: disable=function-redefined, no-self-use, too-many-arguments - op1: Callable[['Observable'], A], - op2: Callable[[A], B], - op3: Callable[[B], C], - op4: Callable[[C], D], - op5: Callable[[D], E]) -> E: - ... # pylint: disable=pointless-statement + def pipe( + self, + op1: Callable[[Observable[_T]], _A], + op2: Callable[[_A], _B], + op3: Callable[[_B], _C], + op4: Callable[[_C], _D], + op5: Callable[[_D], _E], + ) -> _E: + ... @overload - def pipe(self, # pylint: disable=function-redefined, no-self-use, too-many-arguments - op1: Callable[['Observable'], A], - op2: Callable[[A], B], - op3: Callable[[B], C], - op4: Callable[[C], D], - op5: Callable[[D], E], - op6: Callable[[E], F]) -> F: - ... # pylint: disable=pointless-statement + def pipe( + self, # pylint: disable=function-redefined, no-self-use, too-many-arguments + op1: Callable[[Observable[_T]], _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, # pylint: disable=function-redefined, no-self-use, too-many-arguments - op1: Callable[['Observable'], 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: - ... # pylint: disable=pointless-statement + def pipe( + self, # pylint: too-many-arguments + op1: Callable[[Observable[_T]], _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: + ... # pylint: disable=function-redefined - def pipe(self, *operators: Callable[['Observable'], Any]) -> Any: + def pipe(self, *operators: Callable[[Any], Any]) -> Any: """Compose multiple operators left to right. Composes zero or more operators into a functional composition. @@ -262,6 +300,7 @@ def pipe(self, *operators: Callable[['Observable'], Any]) -> Any: The composed observable. """ from ..pipe import pipe + return pipe(*operators)(self) def run(self) -> Any: @@ -283,6 +322,7 @@ def run(self) -> Any: The last element emitted from the observable. """ from ..run import run + return run(self) def __await__(self) -> Any: @@ -292,10 +332,11 @@ def __await__(self) -> Any: The last item of the observable sequence. """ from ..operators.tofuture import _to_future + loop = asyncio.get_event_loop() return iter(self.pipe(_to_future(scheduler=AsyncIOScheduler(loop=loop)))) - def __add__(self, other) -> 'Observable': + def __add__(self, other: Observable[_T]) -> Observable[_T]: """Pythonic version of :func:`concat `. Example: @@ -308,9 +349,10 @@ def __add__(self, other) -> 'Observable': Concatenated observable sequence. """ from rx import concat + return concat(self, other) - def __iadd__(self, other) -> 'Observable': + def __iadd__(self, other: Observable[_T]) -> "Observable[_T]": """Pythonic use of :func:`concat `. Example: @@ -323,9 +365,10 @@ def __iadd__(self, other) -> 'Observable': Concatenated observable sequence. """ from rx import concat + return concat(self, other) - def __getitem__(self, key) -> 'Observable': + def __getitem__(self, key: slice) -> Observable[_T]: """ Pythonic version of :func:`slice `. @@ -370,7 +413,8 @@ def __getitem__(self, key) -> 'Observable': elif isinstance(key, int): start, stop, step = key, key + 1, 1 else: - raise TypeError('Invalid argument type.') + raise TypeError("Invalid argument type.") from ..operators.slice import _slice + return _slice(start, stop, step)(self) diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 0ca6958df..ff0db84f6 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,11 +1,12 @@ -from typing import Union from asyncio import Future +from typing import Union import rx -from rx.scheduler import CurrentThreadScheduler from rx.core import Observable -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) from rx.internal.utils import is_future +from rx.scheduler import CurrentThreadScheduler def _on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: diff --git a/rx/core/observable/range.py b/rx/core/observable/range.py index 0dc2cb7c7..bf43fa9f2 100644 --- a/rx/core/observable/range.py +++ b/rx/core/observable/range.py @@ -1,17 +1,14 @@ from sys import maxsize -from typing import Optional +from typing import Iterator, Optional -from rx.core import typing -from rx.core import Observable -from rx.scheduler import CurrentThreadScheduler +from rx.core import Observable, abc from rx.disposable import MultipleAssignmentDisposable +from rx.scheduler import CurrentThreadScheduler -def _range(start: int, - stop: Optional[int] = None, - step: Optional[int] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Observable: +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. @@ -41,14 +38,15 @@ def _range(start: int, else: range_t = range(start, _stop, _step) - def subscribe(observer, scheduler_: typing.Scheduler = None): + def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): nonlocal range_t _scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton() sd = MultipleAssignmentDisposable() - def action(scheduler, iterator): + def action(scheduler: abc.SchedulerBase, iterator: Optional[Iterator[int]]): try: + assert iterator observer.on_next(next(iterator)) sd.disposable = _scheduler.schedule(action, state=iterator) except StopIteration: @@ -56,4 +54,8 @@ def action(scheduler, iterator): sd.disposable = _scheduler.schedule(action, iter(range_t)) return sd + return Observable(subscribe) + + +__all__ = ["_range"] diff --git a/rx/core/observable/returnvalue.py b/rx/core/observable/returnvalue.py index a88b0afe5..b44cb727c 100644 --- a/rx/core/observable/returnvalue.py +++ b/rx/core/observable/returnvalue.py @@ -1,12 +1,12 @@ -from typing import Any, Callable, Optional +from typing import Any, Callable, Optional, TypeVar -from rx.core import typing -from rx.core import Observable +from rx.core import Observable, abc from rx.scheduler import CurrentThreadScheduler -from rx.core.abc.scheduler import Scheduler +_T = TypeVar("_T") -def _return_value(value: Any, scheduler: Optional[typing.Scheduler] = None) -> Observable: + +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'. @@ -23,22 +23,23 @@ def _return_value(value: Any, scheduler: Optional[typing.Scheduler] = None) -> O element. """ - def subscribe(observer: typing.Observer, scheduler_: Optional[typing.Scheduler] = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton() - def action(scheduler: typing.Scheduler, state: Any = None): + def action(scheduler: abc.SchedulerBase, state: Any = None): observer.on_next(value) observer.on_completed() return _scheduler.schedule(action) + return Observable(subscribe) -def _from_callable(supplier: Callable[[], Any], scheduler: Optional[typing.Scheduler] = None) -> Observable: - def subscribe(observer: typing.Observer, scheduler_: typing.Scheduler = None): +def _from_callable(supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton() - def action(_: Scheduler, __: Any = None): + def action(_: abc.SchedulerBase, __: Any = None): nonlocal observer try: @@ -46,6 +47,10 @@ def action(_: Scheduler, __: Any = None): 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"] diff --git a/rx/core/observable/start.py b/rx/core/observable/start.py index 74fe7a2e1..ddefa4bbb 100644 --- a/rx/core/observable/start.py +++ b/rx/core/observable/start.py @@ -1,10 +1,10 @@ -from typing import Optional, Callable +from typing import Any, Callable, Optional from rx import to_async -from rx.core import Observable -from rx.core.typing import Scheduler +from rx.core import Observable, abc -def _start(func: Callable, scheduler: Optional[Scheduler] = None) -> Observable: + +def _start(func: Callable[..., None], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: """Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence. @@ -28,3 +28,6 @@ def _start(func: Callable, scheduler: Optional[Scheduler] = None) -> Observable: """ return to_async(func, scheduler)() + + +__all__ = ["_start"] diff --git a/rx/core/observable/startasync.py b/rx/core/observable/startasync.py index ee2b3b4e6..0bf8fd88b 100644 --- a/rx/core/observable/startasync.py +++ b/rx/core/observable/startasync.py @@ -1,7 +1,7 @@ -from typing import Callable from asyncio import Future +from typing import Callable -from rx import throw, from_future +from rx import from_future, throw from rx.core import Observable diff --git a/rx/core/observable/throw.py b/rx/core/observable/throw.py index 1868fc192..74567436e 100644 --- a/rx/core/observable/throw.py +++ b/rx/core/observable/throw.py @@ -1,19 +1,18 @@ from typing import Any, Optional -from rx.core import typing -from rx.core import Observable - +from rx.core import Observable, abc from rx.scheduler import ImmediateScheduler -def _throw(exception: Exception, scheduler: Optional[typing.Scheduler] = None) -> Observable: +def _throw(exception: Exception, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: exception = exception if isinstance(exception, Exception) else Exception(exception) - def subscribe(observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: _scheduler = scheduler or ImmediateScheduler.singleton() - def action(scheduler: typing.Scheduler, state: Any): + def action(scheduler: abc.SchedulerBase, state: Any): observer.on_error(exception) return _scheduler.schedule(action) + return Observable(subscribe) diff --git a/rx/core/observable/timer.py b/rx/core/observable/timer.py index 5912bd473..8081ea6d4 100644 --- a/rx/core/observable/timer.py +++ b/rx/core/observable/timer.py @@ -1,25 +1,30 @@ from datetime import datetime -from typing import Optional +from typing import Any, Optional -from rx.scheduler import TimeoutScheduler -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import MultipleAssignmentDisposable +from rx.scheduler import TimeoutScheduler -def observable_timer_date(duetime, scheduler: Optional[typing.Scheduler] = None): - def subscribe(observer, scheduler_=None): - _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() +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): + _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any): observer.on_next(0) observer.on_completed() return _scheduler.schedule_absolute(duetime, action) + return Observable(subscribe) -def observable_timer_duetime_and_period(duetime, period, scheduler: Optional[typing.Scheduler] = None) -> Observable: - def subscribe(observer, scheduler_=None): +def observable_timer_duetime_and_period( + duetime: typing.AbsoluteTime, period: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[int]: + def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() nonlocal duetime @@ -31,7 +36,7 @@ def subscribe(observer, scheduler_=None): dt = duetime count = 0 - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any): nonlocal dt nonlocal count @@ -44,47 +49,56 @@ def action(scheduler, state): 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[typing.Scheduler] = None) -> Observable: - def subscribe(observer, scheduler_=None): +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): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() d = _scheduler.to_seconds(duetime) - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any): 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[typing.Scheduler] = None - ) -> Observable: +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, scheduler_=None): + + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - def action(count): + def action(count: int): observer.on_next(count) return count + 1 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[typing.Scheduler] = None - ) -> Observable: +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) @@ -95,3 +109,6 @@ def _timer(duetime: typing.AbsoluteOrRelativeTime, return observable_timer_timespan(duetime, scheduler) return observable_timer_timespan_and_period(duetime, period, scheduler) + + +__all__ = ["_timer"] diff --git a/rx/core/observable/toasync.py b/rx/core/observable/toasync.py index d42cb5f75..f8d90d7fd 100644 --- a/rx/core/observable/toasync.py +++ b/rx/core/observable/toasync.py @@ -1,15 +1,12 @@ from typing import Callable, Optional from rx import operators as ops -from rx.core import Observable -from rx.core.typing import Scheduler +from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler from rx.subject import AsyncSubject -def _to_async(func: Callable, - scheduler: Optional[Scheduler] = None - ) -> Callable: +def _to_async(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Callable: """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 @@ -46,4 +43,5 @@ def action(scheduler, state): _scheduler.schedule(action) return subject.pipe(ops.as_observable()) + return wrapper diff --git a/rx/core/observable/using.py b/rx/core/observable/using.py index ee59a94df..fc6b9d17d 100644 --- a/rx/core/observable/using.py +++ b/rx/core/observable/using.py @@ -1,14 +1,13 @@ from typing import Callable import rx -from rx.core import Observable -from rx.core import typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, Disposable -def _using(resource_factory: Callable[[], typing.Disposable], - observable_factory: Callable[[typing.Disposable], Observable] - ) -> Observable: +def _using( + resource_factory: Callable[[], abc.DisposableBase], observable_factory: Callable[[abc.DisposableBase], Observable] +) -> Observable: """Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime. @@ -40,4 +39,5 @@ def subscribe(observer, scheduler=None): return CompositeDisposable(d, disp) return CompositeDisposable(source.subscribe(observer, scheduler=scheduler), disp) + return Observable(subscribe) diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index 139803688..9a43b6a8b 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -1,6 +1,5 @@ -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable - from rx.core import Observable +from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import NotSet diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index 8b7564771..f00db7ad6 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -1,16 +1,16 @@ from threading import RLock -from typing import Optional, List +from typing import Any, List, Optional, Tuple from rx import from_future -from rx.core import Observable, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.concurrency import synchronized from rx.internal.utils import is_future - # pylint: disable=redefined-builtin -def _zip(*args: Observable) -> Observable: + +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 @@ -29,14 +29,15 @@ def _zip(*args: Observable) -> Observable: sources = list(args) - def subscribe(observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None) -> CompositeDisposable: + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> CompositeDisposable: n = len(sources) - queues: List[List] = [[] for _ in range(n)] + queues: List[List[Any]] = [[] for _ in range(n)] lock = RLock() @synchronized(lock) - def next(i): + def next(i: int): if all([len(q) for q in queues]): try: queued_values = [x.pop(0) for x in queues] @@ -47,14 +48,14 @@ def next(i): observer.on_next(res) - subscriptions = [None] * n + subscriptions: List[Optional[abc.DisposableBase]] = [None] * n - def func(i): + def func(i: int): source = sources[i] sad = SingleAssignmentDisposable() - source = from_future(source) if is_future(source) else source + source: Observable[Any] = from_future(source) if is_future(source) else source - def on_next(x): + def on_next(x: Any): queues[i].append(x) next(i) diff --git a/rx/core/observer/__init__.py b/rx/core/observer/__init__.py index d684d44b9..f6f084d36 100644 --- a/rx/core/observer/__init__.py +++ b/rx/core/observer/__init__.py @@ -1,4 +1,4 @@ +from .autodetachobserver import AutoDetachObserver +from .observeonobserver import ObserveOnObserver from .observer import Observer from .scheduledobserver import ScheduledObserver -from .observeonobserver import ObserveOnObserver -from .autodetachobserver import AutoDetachObserver diff --git a/rx/core/observer/autodetachobserver.py b/rx/core/observer/autodetachobserver.py index 661182f26..a4051974b 100644 --- a/rx/core/observer/autodetachobserver.py +++ b/rx/core/observer/autodetachobserver.py @@ -1,18 +1,20 @@ -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.internal import noop, default_error from rx.disposable import SingleAssignmentDisposable +from rx.internal import default_error, noop -from .. import typing +from .. import abc, typing +_T_in = TypeVar("_T_in", contravariant=True) -class AutoDetachObserver(typing.Observer): - def __init__(self, - on_next: Optional[typing.OnNext] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None - ) -> None: +class AutoDetachObserver(abc.ObserverBase[_T_in]): + def __init__( + self, + on_next: Optional[typing.OnNext] = None, + on_error: Optional[typing.OnError] = None, + on_completed: Optional[typing.OnCompleted] = None, + ) -> None: self._on_next = on_next or noop self._on_error = on_error or default_error self._on_completed = on_completed or noop @@ -20,12 +22,12 @@ def __init__(self, self._subscription = SingleAssignmentDisposable() self.is_stopped = False - def on_next(self, value: Any) -> None: + def on_next(self, value: _T_in) -> None: if self.is_stopped: return self._on_next(value) - def on_error(self, error) -> None: + def on_error(self, error: Exception) -> None: if self.is_stopped: return self.is_stopped = True @@ -45,7 +47,7 @@ def on_completed(self) -> None: finally: self.dispose() - def set_disposable(self, value: typing.Disposable): + def set_disposable(self, value: abc.DisposableBase): self._subscription.disposable = value subscription = property(fset=set_disposable) diff --git a/rx/core/observer/observer.py b/rx/core/observer/observer.py index f5bb6acb5..1ec2101ff 100644 --- a/rx/core/observer/observer.py +++ b/rx/core/observer/observer.py @@ -1,31 +1,44 @@ -from typing import Any, Callable, Optional +from __future__ import annotations -from .. import typing -from rx.internal import noop, default_error +from typing import TYPE_CHECKING, Callable, Optional, TypeVar +from rx.core import abc +from rx.core.typing import OnCompleted, OnError, OnNext +from rx.internal.basic import default_error, noop -class Observer(typing.Observer, typing.Disposable): +_T_in = TypeVar("_T_in", contravariant=True) + +if TYPE_CHECKING: + from rx.core.notification import Notification +else: + + class Notification: + pass + + +class Observer(abc.ObserverBase[_T_in], abc.DisposableBase): """Base class for implementations of the Observer class. This base class enforces the grammar of observers where OnError and OnCompleted are terminal messages. """ - def __init__(self, - on_next: Optional[typing.OnNext] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None - ) -> None: + def __init__( + self, + on_next: Optional[OnNext[_T_in]] = None, + on_error: Optional[OnError] = None, + on_completed: Optional[OnCompleted] = None, + ) -> None: self.is_stopped = False - self._handler_on_next = on_next or noop - self._handler_on_error = on_error or default_error - self._handler_on_completed = on_completed or noop + self._handler_on_next: OnNext[_T_in] = on_next or noop + self._handler_on_error: OnError = on_error or default_error + self._handler_on_completed: OnCompleted = on_completed or noop - def on_next(self, value: Any) -> None: + def on_next(self, value: _T_in) -> None: """Notify the observer of a new element in the sequence.""" if not self.is_stopped: self._on_next_core(value) - def _on_next_core(self, value: Any) -> None: + def _on_next_core(self, value: _T_in) -> None: """For Subclassing purpose. This method is called by `on_next()` method until the observer is stopped. """ @@ -76,22 +89,24 @@ def fail(self, exn: Exception) -> bool: def throw(self, error: Exception) -> None: import traceback + traceback.print_stack() if error: raise error 1 / 0 # Raise division by zero - def to_notifier(self) -> Callable: + def to_notifier(self) -> Callable[[Notification[_T_in]], None]: """Creates a notification callback from an observer. Returns the action that forwards its input notification to the underlying observer.""" - def func(notifier): + def func(notifier: Notification[_T_in]) -> None: return notifier.accept(self) + return func - def as_observer(self) -> 'Observer': + def as_observer(self) -> abc.ObserverBase[_T_in]: """Hides the identity of an observer. Returns an observer that hides the identity of the specified diff --git a/rx/core/observer/scheduledobserver.py b/rx/core/observer/scheduledobserver.py index e907cb618..e7defc9f8 100644 --- a/rx/core/observer/scheduledobserver.py +++ b/rx/core/observer/scheduledobserver.py @@ -1,14 +1,16 @@ import threading -from typing import List, Any +from typing import Any, List, TypeVar -from rx.core import typing +from rx.core import abc, typing from rx.disposable import SerialDisposable from .observer import Observer +_T_in = TypeVar("_T_in", contravariant=True) -class ScheduledObserver(Observer): - def __init__(self, scheduler: typing.Scheduler, observer: typing.Observer) -> None: + +class ScheduledObserver(Observer[_T_in]): + def __init__(self, scheduler: abc.SchedulerBase, observer: abc.ObserverBase[_T_in]) -> None: super().__init__() self.scheduler = scheduler @@ -26,16 +28,19 @@ def __init__(self, scheduler: typing.Scheduler, observer: typing.Observer) -> No def _on_next_core(self, value: Any) -> None: def action(): self.observer.on_next(value) + self.queue.append(action) def _on_error_core(self, error: Exception) -> None: def action(): self.observer.on_error(error) + self.queue.append(action) def _on_completed_core(self) -> None: def action(): self.observer.on_completed() + self.queue.append(action) def ensure_active(self) -> None: @@ -49,7 +54,7 @@ def ensure_active(self) -> None: if is_owner: self.disposable.disposable = self.scheduler.schedule(self.run) - def run(self, scheduler: typing.Scheduler, state: typing.TState) -> None: + def run(self, scheduler: abc.SchedulerBase, state: Any) -> None: parent = self with self.lock: diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 0b1e3052a..decf2e692 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -1,8 +1,8 @@ from asyncio import Future -from typing import cast, Any, Union +from typing import Any, Union, cast from rx import from_future -from rx.core import Observable, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import is_future @@ -15,10 +15,10 @@ def _amb(right_source: Observable): obs = cast(Observable, right_source) def amb(left_source: Observable): - def subscribe(observer: typing.Observer, scheduler: typing.Scheduler = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase, scheduler: abc.SchedulerBase = None) -> abc.DisposableBase: choice = [None] - left_choice = 'L' - right_choice = 'R' + left_choice = "L" + right_choice = "R" left_subscription = SingleAssignmentDisposable() right_subscription = SingleAssignmentDisposable() @@ -74,5 +74,7 @@ def on_completed_right() -> None: right_d = obs.subscribe_(send_right, on_error_right, on_completed_right, scheduler) right_subscription.disposable = right_d return CompositeDisposable(left_subscription, right_subscription) + return Observable(subscribe) - return amb \ No newline at end of file + + return amb diff --git a/rx/core/operators/asobservable.py b/rx/core/operators/asobservable.py index 6003fa743..879eb20b0 100644 --- a/rx/core/operators/asobservable.py +++ b/rx/core/operators/asobservable.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index 96cfbb025..febbc604e 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Any +from typing import Any, Callable, Optional from rx import operators as ops from rx.core import Observable, pipe diff --git a/rx/core/operators/bufferwithtime.py b/rx/core/operators/bufferwithtime.py index 0ef621362..0873d4dcd 100644 --- a/rx/core/operators/bufferwithtime.py +++ b/rx/core/operators/bufferwithtime.py @@ -1,15 +1,15 @@ from typing import Callable, Optional from rx import operators as ops -from rx.core import Observable, pipe, typing +from rx.core import Observable, abc, pipe, typing -def _buffer_with_time(timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def _buffer_with_time( + timespan: typing.RelativeTime, + timeshift: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: if not timeshift: timeshift = timespan - return pipe( - ops.window_with_time(timespan, timeshift, scheduler), - ops.flat_map(lambda x: x.pipe(ops.to_iterable())) - ) + return pipe(ops.window_with_time(timespan, timeshift, scheduler), ops.flat_map(lambda x: x.pipe(ops.to_iterable()))) diff --git a/rx/core/operators/catch.py b/rx/core/operators/catch.py index 27b6252cf..bccdd6eaf 100644 --- a/rx/core/operators/catch.py +++ b/rx/core/operators/catch.py @@ -1,8 +1,8 @@ from typing import Callable, Union import rx -from rx.core import Observable, typing -from rx.disposable import SingleAssignmentDisposable, SerialDisposable +from rx.core import Observable, abc, typing +from rx.disposable import SerialDisposable, SingleAssignmentDisposable from rx.internal.utils import is_future @@ -25,18 +25,15 @@ def on_error(exception): subscription.disposable = d d.disposable = result.subscribe(observer, scheduler=scheduler) - d1.disposable = source.subscribe_( - observer.on_next, - on_error, - observer.on_completed, - scheduler - ) + d1.disposable = source.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler) return subscription + return Observable(subscribe) -def _catch(handler: Union[Observable, Callable[[Exception, Observable], Observable]] - ) -> Callable[[Observable], Observable]: +def _catch( + handler: Union[Observable, Callable[[Exception, Observable], Observable]] +) -> Callable[[Observable], Observable]: def catch(source: Observable) -> Observable: """Continues an observable sequence that is terminated by an exception with the next observable sequence. @@ -59,8 +56,9 @@ def catch(source: Observable) -> Observable: """ if callable(handler): return catch_handler(source, handler) - elif isinstance(handler, typing.Observable): + elif isinstance(handler, abc.ObservableBase): return rx.catch(source, handler) else: - raise TypeError('catch operator takes whether an Observable or a callable handler as argument.') + raise TypeError("catch operator takes whether an Observable or a callable handler as argument.") + return catch diff --git a/rx/core/operators/combinelatest.py b/rx/core/operators/combinelatest.py index 9a2c29b4e..2937353a5 100644 --- a/rx/core/operators/combinelatest.py +++ b/rx/core/operators/combinelatest.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Iterable, Union, List +from typing import Any, Callable, Iterable, List, Union import rx from rx.core import Observable, typing diff --git a/rx/core/operators/connectable/refcount.py b/rx/core/operators/connectable/refcount.py index 847c6da8e..08540c508 100644 --- a/rx/core/operators/connectable/refcount.py +++ b/rx/core/operators/connectable/refcount.py @@ -1,7 +1,7 @@ from typing import Callable -from rx.disposable import Disposable from rx.core import ConnectableObservable, Observable +from rx.disposable import Disposable def _ref_count() -> Callable[[ConnectableObservable], Observable]: diff --git a/rx/core/operators/count.py b/rx/core/operators/count.py index bfbc56307..e59825c71 100644 --- a/rx/core/operators/count.py +++ b/rx/core/operators/count.py @@ -1,8 +1,8 @@ from typing import Callable, Optional -from rx.core import Observable, pipe -from rx.core.typing import Predicate from rx import operators as ops +from rx.core import Observable, pipe +from rx.core.typing import Predicate def _count(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/debounce.py b/rx/core/operators/debounce.py index db74e1db3..7350861d7 100644 --- a/rx/core/operators/debounce.py +++ b/rx/core/operators/debounce.py @@ -1,13 +1,15 @@ -from typing import Callable, Any +from typing import Any, Callable, TypeVar -from rx.core.typing import Disposable -from rx.core import Observable, typing -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _debounce(duetime: typing.RelativeTime, scheduler=typing.Scheduler) -> Callable[[Observable], Observable]: - def debounce(source: Observable) -> Observable: + +def _debounce(duetime: typing.RelativeTime, scheduler: abc.SchedulerBase) -> Callable[[Observable[_T]], Observable[_T]]: + def debounce(source: Observable[_T]) -> Observable[_T]: """Ignores values from an observable sequence which are followed by another value before duetime. @@ -22,7 +24,7 @@ def debounce(source: Observable) -> Observable: returns the debounced observable sequence. """ - def subscribe(observer, scheduler_=None) -> Disposable: + def subscribe(observer, scheduler_=None) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() cancelable = SerialDisposable() has_value = [False] @@ -61,12 +63,16 @@ def on_completed() -> None: subscription = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler_) return CompositeDisposable(subscription, cancelable) + return Observable(subscribe) + return debounce -def _throttle_with_mapper(throttle_duration_mapper: Callable[[Any], Observable]) -> Callable[[Observable], Observable]: - def throttle_with_mapper(source: Observable) -> Observable: +def _throttle_with_mapper( + throttle_duration_mapper: Callable[[Any], Observable[_T]] +) -> Callable[[Observable[_T]], Observable[_T]]: + def throttle_with_mapper(source: Observable[_T]) -> Observable[_T]: """Partially applied throttle_with_mapper operator. Ignores values from an observable sequence which are followed by @@ -81,7 +87,8 @@ def throttle_with_mapper(source: Observable) -> Observable: Returns: The throttled observable sequence. """ - def subscribe(observer, scheduler=None) -> Disposable: + + def subscribe(observer, scheduler=None) -> abc.DisposableBase: cancelable = SerialDisposable() has_value = [False] value = [None] @@ -135,5 +142,7 @@ def on_completed() -> None: subscription = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) return CompositeDisposable(subscription, cancelable) + return Observable(subscribe) + return throttle_with_mapper diff --git a/rx/core/operators/defaultifempty.py b/rx/core/operators/defaultifempty.py index c4affe870..3e6ac9436 100644 --- a/rx/core/operators/defaultifempty.py +++ b/rx/core/operators/defaultifempty.py @@ -1,6 +1,7 @@ from typing import Any, Callable + from rx.core import Observable -from rx.core.typing import Disposable +from rx.core.abc import DisposableBase def _default_if_empty(default_value: Any = None) -> Callable[[Observable], Observable]: @@ -21,7 +22,7 @@ def default_if_empty(source: Observable) -> Observable: source. """ - def subscribe(observer, scheduler=None) -> Disposable: + def subscribe(observer, scheduler=None) -> DisposableBase: found = [False] def on_next(x: Any): @@ -34,5 +35,7 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return Observable(subscribe) + return default_if_empty diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index c0ce9c459..251536caa 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -1,12 +1,15 @@ -from typing import Callable, Optional from datetime import datetime, timedelta +from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, typing +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, MultipleAssignmentDisposable, + SerialDisposable) from rx.internal.constants import DELTA_ZERO -from rx.disposable import CompositeDisposable, SerialDisposable, MultipleAssignmentDisposable from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") + class Timestamp(object): def __init__(self, value, timestamp): @@ -14,10 +17,10 @@ def __init__(self, value, timestamp): self.timestamp = timestamp -def observable_delay_timespan(source: Observable, duetime: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None) -> Observable: - - def subscribe(observer, scheduler_=None): +def observable_delay_timespan( + source: Observable[_T], duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[_T]: + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): nonlocal duetime _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() @@ -37,7 +40,7 @@ def on_next(notification): should_run = False with source.lock: - if notification.value.kind == 'E': + if notification.value.kind == "E": del queue[:] queue.append(notification) exception[0] = notification.value.exception @@ -90,17 +93,18 @@ def action(scheduler, state): mad.disposable = scheduler.schedule_relative(recurse_duetime, action) mad.disposable = _scheduler.schedule_relative(duetime, action) - subscription = source.pipe( - ops.materialize(), - ops.timestamp() - ).subscribe_(on_next, scheduler=_scheduler) + + subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe_(on_next, scheduler=_scheduler) return CompositeDisposable(subscription, cancelable) + return Observable(subscribe) -def _delay(duetime: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: - def delay(source: Observable) -> Observable: +def _delay( + duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def delay(source: Observable[_T]) -> Observable[_T]: """Time shifts the observable sequence. A partially applied delay operator function. @@ -115,4 +119,8 @@ def delay(source: Observable) -> Observable: A time-shifted observable sequence. """ return observable_delay_timespan(source, duetime, scheduler) + return delay + + +__all__ = ["_delay"] diff --git a/rx/core/operators/delaysubscription.py b/rx/core/operators/delaysubscription.py index 1c965f359..d3b52d1f8 100644 --- a/rx/core/operators/delaysubscription.py +++ b/rx/core/operators/delaysubscription.py @@ -5,9 +5,9 @@ from rx.core import Observable, typing -def _delay_subscription(duetime: typing.AbsoluteOrRelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _delay_subscription( + duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def delay_subscription(source: Observable) -> Observable: """Time shifts the observable sequence by delaying the subscription. @@ -24,7 +24,6 @@ def delay_subscription(source: Observable) -> Observable: def mapper(_) -> Observable: return rx.empty() - return source.pipe( - ops.delay_with_mapper(rx.timer(duetime, scheduler=scheduler), mapper) - ) + return source.pipe(ops.delay_with_mapper(rx.timer(duetime, scheduler=scheduler), mapper)) + return delay_subscription diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index aa6fc7e1f..2881d8678 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -1,7 +1,8 @@ from typing import Callable -from rx.core import Observable, typing -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) def _delay_with_mapper(subscription_delay=None, delay_duration_mapper=None) -> Callable[[Observable], Observable]: @@ -25,7 +26,7 @@ def delay_with_mapper(source: Observable) -> Observable: sub_delay, mapper = None, None - if isinstance(subscription_delay, typing.Observable): + if isinstance(subscription_delay, abc.ObservableBase): mapper = delay_duration_mapper sub_delay = subscription_delay else: @@ -36,7 +37,7 @@ def subscribe(observer, scheduler=None): at_end = [False] def done(): - if (at_end[0] and delays.length == 0): + if at_end[0] and delays.length == 0: observer.on_completed() subscription = SerialDisposable() @@ -77,5 +78,7 @@ def on_completed(): subscription.disposable = sub_delay.subscribe_(lambda _: start(), observer.on_error, start) return CompositeDisposable(subscription, delays) + return Observable(subscribe) - return delay_with_mapper \ No newline at end of file + + return delay_with_mapper diff --git a/rx/core/operators/dematerialize.py b/rx/core/operators/dematerialize.py index 1e7ddfda5..1117df143 100644 --- a/rx/core/operators/dematerialize.py +++ b/rx/core/operators/dematerialize.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index ebbc27c87..d0fe31454 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -1,6 +1,7 @@ from typing import Callable, Optional + from rx.core import Observable -from rx.core.typing import Mapper, Comparer +from rx.core.typing import Comparer, Mapper from rx.internal.basic import default_comparer diff --git a/rx/core/operators/distinctuntilchanged.py b/rx/core/operators/distinctuntilchanged.py index 643ff488c..5092086b3 100644 --- a/rx/core/operators/distinctuntilchanged.py +++ b/rx/core/operators/distinctuntilchanged.py @@ -1,7 +1,8 @@ from typing import Callable, Optional + from rx.core import Observable -from rx.core.typing import Mapper, Comparer -from rx.internal.basic import identity, default_comparer +from rx.core.typing import Comparer, Mapper +from rx.internal.basic import default_comparer, identity def _distinct_until_changed( diff --git a/rx/core/operators/do.py b/rx/core/operators/do.py index 808d2333c..5d720a1a3 100644 --- a/rx/core/operators/do.py +++ b/rx/core/operators/do.py @@ -1,14 +1,14 @@ from typing import Callable, Optional -from rx.core import Observable, typing -from rx.core.typing import Observer, Disposable +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable -def _do_action(on_next: Optional[typing.OnNext] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None - ) -> Callable[[Observable], Observable]: +def _do_action( + on_next: Optional[typing.OnNext] = None, + on_error: Optional[typing.OnError] = None, + on_completed: Optional[typing.OnCompleted] = None, +) -> Callable[[Observable], Observable]: def do_action(source: Observable) -> Observable: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional @@ -64,17 +64,19 @@ def _on_completed(): else: try: on_completed() - except Exception as e: # pylint: disable=broad-except + except Exception as e: # pylint: disable=broad-except observer.on_error(e) observer.on_completed() return source.subscribe_(_on_next, _on_error, _on_completed, scheduler) + return Observable(subscribe) + return do_action -def do(observer: Observer) -> Callable[[Observable], Observable]: +def do(observer: abc.ObserverBase) -> Callable[[Observable], Observable]: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional termination of the observable sequence. This method can be used for debugging, logging, @@ -103,7 +105,6 @@ def do_after_next(source, after_next): """ def subscribe(observer, scheduler=None): - def on_next(value): try: observer.on_next(value) @@ -112,6 +113,7 @@ def on_next(value): observer.on_error(e) return source.subscribe_(on_next, observer.on_error, observer.on_completed) + return Observable(subscribe) @@ -124,6 +126,7 @@ def do_on_subscribe(source: Observable, on_subscribe): Args: on_subscribe: Action to invoke on subscription """ + def subscribe(observer, scheduler=None): on_subscribe() return source.subscribe_(observer.on_next, observer.on_error, observer.on_completed, scheduler) @@ -142,7 +145,6 @@ def do_on_dispose(source: Observable, on_dispose): """ class OnDispose(Disposable): - def dispose(self) -> None: on_dispose() @@ -166,7 +168,6 @@ def do_on_terminate(source, on_terminate): """ def subscribe(observer, scheduler=None): - def on_completed(): try: on_terminate() @@ -184,6 +185,7 @@ def on_error(exception): observer.on_error(exception) return source.subscribe_(observer.on_next, on_error, on_completed, scheduler) + return Observable(subscribe) @@ -195,8 +197,8 @@ def do_after_terminate(source, after_terminate): on_terminate -- Action to invoke after on_complete or throw is called """ - def subscribe(observer, scheduler=None): + def subscribe(observer, scheduler=None): def on_completed(): observer.on_completed() try: @@ -212,6 +214,7 @@ def on_error(exception): observer.on_error(err) return source.subscribe(observer.on_next, on_error, on_completed, scheduler) + return Observable(subscribe) @@ -270,4 +273,5 @@ def on_error(exception): return composite_disposable return Observable(subscribe) + return partial diff --git a/rx/core/operators/dowhile.py b/rx/core/operators/dowhile.py index da52e3b78..33dc74823 100644 --- a/rx/core/operators/dowhile.py +++ b/rx/core/operators/dowhile.py @@ -1,4 +1,4 @@ -from typing import Callable, Any +from typing import Any, Callable from rx import operators as ops from rx.core import Observable diff --git a/rx/core/operators/elementatordefault.py b/rx/core/operators/elementatordefault.py index 06cec4e80..d73560ce2 100644 --- a/rx/core/operators/elementatordefault.py +++ b/rx/core/operators/elementatordefault.py @@ -1,4 +1,5 @@ from typing import Any, Callable + from rx.core import Observable from rx.internal.exceptions import ArgumentOutOfRangeException diff --git a/rx/core/operators/expand.py b/rx/core/operators/expand.py index d9d2ebee4..c7ec15cb6 100644 --- a/rx/core/operators/expand.py +++ b/rx/core/operators/expand.py @@ -2,7 +2,8 @@ from rx.core import Observable from rx.core.typing import Mapper -from rx.disposable import SerialDisposable, CompositeDisposable, SingleAssignmentDisposable +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) from rx.scheduler import ImmediateScheduler diff --git a/rx/core/operators/filter.py b/rx/core/operators/filter.py index f47a3e376..19354a899 100644 --- a/rx/core/operators/filter.py +++ b/rx/core/operators/filter.py @@ -1,12 +1,13 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar -from rx.core import Observable -from rx.core.typing import Predicate, PredicateIndexed, Scheduler, Observer, Disposable +from rx.core import Observable, abc +from rx.core.typing import Predicate, PredicateIndexed +_T = TypeVar("_T") # pylint: disable=redefined-builtin -def _filter(predicate: Predicate) -> Callable[[Observable], Observable]: - def filter(source: Observable) -> Observable: +def _filter(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: + def filter(source: Observable[_T]) -> Observable[_T]: """Partially applied filter operator. Filters the elements of an observable sequence based on a @@ -22,8 +23,8 @@ def filter(source: Observable) -> Observable: A filtered observable sequence. """ - def subscribe(observer: Observer, scheduler: Optional[Scheduler]) -> Disposable: - def on_next(value): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase]) -> abc.DisposableBase: + def on_next(value: _T): try: should_run = predicate(value) except Exception as ex: # pylint: disable=broad-except @@ -34,12 +35,16 @@ def on_next(value): observer.on_next(value) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return Observable(subscribe) + return filter -def _filter_indexed(predicate_indexed: Optional[PredicateIndexed] = None) -> Callable[[Observable], Observable]: - def filter_indexed(source: Observable) -> Observable: +def _filter_indexed( + predicate_indexed: Optional[PredicateIndexed[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def filter_indexed(source: Observable[_T]) -> Observable[_T]: """Partially applied indexed filter operator. Filters the elements of an observable sequence based on a @@ -55,10 +60,10 @@ def filter_indexed(source: Observable) -> Observable: A filtered observable sequence. """ - def subscribe(observer: Observer, scheduler: Optional[Scheduler]): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase]): count = 0 - def on_next(value): + def on_next(value: _T): nonlocal count try: should_run = predicate_indexed(value, count) @@ -72,5 +77,10 @@ def on_next(value): observer.on_next(value) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return Observable(subscribe) + return filter_indexed + + +__all__ = ["_filter", "_filter_indexed"] diff --git a/rx/core/operators/finallyaction.py b/rx/core/operators/finallyaction.py index a517992e0..bb6da49bb 100644 --- a/rx/core/operators/finallyaction.py +++ b/rx/core/operators/finallyaction.py @@ -1,7 +1,7 @@ from typing import Callable -from rx.disposable import Disposable from rx.core import Observable +from rx.disposable import Disposable def _finally_action(action: Callable) -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/find.py b/rx/core/operators/find.py index 1a21e9e79..ddb2822f5 100644 --- a/rx/core/operators/find.py +++ b/rx/core/operators/find.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable from rx.core.typing import Predicate diff --git a/rx/core/operators/first.py b/rx/core/operators/first.py index 39d0147d8..37558a1e1 100644 --- a/rx/core/operators/first.py +++ b/rx/core/operators/first.py @@ -1,4 +1,5 @@ from typing import Callable, Optional + from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Predicate diff --git a/rx/core/operators/firstordefault.py b/rx/core/operators/firstordefault.py index b463f6ff6..e693de682 100644 --- a/rx/core/operators/firstordefault.py +++ b/rx/core/operators/firstordefault.py @@ -1,15 +1,19 @@ -from typing import Any, Callable, Optional +from typing import Any, Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, pipe +from rx.core import Observable, abc, pipe from rx.core.typing import Predicate from rx.internal.exceptions import SequenceContainsNoElementsError +_T = TypeVar("_T") -def _first_or_default_async(has_default=False, default_value=None): - def first_or_default_async(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - def on_next(x): + +def _first_or_default_async( + has_default: bool = False, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def first_or_default_async(source: Observable[_T]) -> Observable[_T]: + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def on_next(x: _T): observer.on_next(x) observer.on_completed() @@ -21,13 +25,15 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return Observable(subscribe) + return first_or_default_async -def _first_or_default(predicate: Optional[Predicate] = None, - default_value: Any = None - ) -> Callable[[Observable], Observable]: +def _first_or_default( + predicate: Optional[Predicate[_T]] = None, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the first element of an observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. @@ -53,8 +59,8 @@ def _first_or_default(predicate: Optional[Predicate] = None, """ if predicate: - return pipe( - ops.filter(predicate), - ops.first_or_default(None, default_value) - ) + return pipe(ops.filter(predicate), ops.first_or_default(None, default_value)) return _first_or_default_async(True, default_value) + + +__all__ = ["_first_or_default", "_first_or_default_async"] diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index f02626589..ee8958826 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -1,15 +1,23 @@ import collections -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar -from rx import from_, from_future, operators as ops +from rx import from_, from_future +from rx import operators as ops from rx.core import Observable from rx.core.typing import Mapper, MapperIndexed from rx.internal.utils import is_future +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") -def _flat_map_internal(source, mapper=None, mapper_indexed=None): - def projection(x, i): - mapper_result = mapper(x) if mapper else mapper_indexed(x, i) + +def _flat_map_internal( + source: Observable[_T1], + mapper: Optional[Mapper[_T1, Observable[_T2]]] = None, + mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, +) -> Observable[_T2]: + def projection(x: _T1, i: int): + mapper_result = mapper(x) if mapper else mapper_indexed(x, i) if mapper_indexed else None if is_future(mapper_result): result = from_future(mapper_result) elif isinstance(mapper_result, collections.abc.Iterable): @@ -18,14 +26,11 @@ def projection(x, i): result = mapper_result return result - return source.pipe( - ops.map_indexed(projection), - ops.merge_all() - ) + return source.pipe(ops.map_indexed(projection), ops.merge_all()) -def _flat_map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: - def flat_map(source: Observable) -> Observable: +def _flat_map(mapper: Optional[Mapper[_T1, Observable[_T2]]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: + def flat_map(source: Observable[_T1]) -> Observable[_T2]: """One of the Following: Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one @@ -50,11 +55,14 @@ def flat_map(source: Observable) -> Observable: ret = _flat_map_internal(source, mapper=lambda _: mapper) return ret + return flat_map -def _flat_map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable[[Observable], Observable]: - def flat_map_indexed(source: Observable) -> Observable: +def _flat_map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, +) -> Callable[[Observable[_T1]], Observable[_T2]]: + def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: """One of the Following: Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one @@ -77,11 +85,12 @@ def flat_map_indexed(source: Observable) -> Observable: else: ret = _flat_map_internal(source, mapper=lambda _: mapper_indexed) return ret + return flat_map_indexed -def _flat_map_latest(mapper: Mapper) -> Callable[[Observable], Observable]: - def flat_map_latest(source: Observable) -> Observable: +def _flat_map_latest(mapper: Mapper[_T1, Observable[_T2]]) -> Callable[[Observable[_T1]], Observable[_T2]]: + def flat_map_latest(source: Observable[_T1]) -> Observable[_T2]: """Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's index and then transforms an observable sequence of observable @@ -99,8 +108,9 @@ def flat_map_latest(source: Observable) -> Observable: inner observable sequence that has been received. """ - return source.pipe( - ops.map(mapper), - ops.switch_latest() - ) + return source.pipe(ops.map(mapper), ops.switch_latest()) + return flat_map_latest + + +__all__ = ["_flat_map", "_flat_map_latest", "_flat_map_indexed"] diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index e61336837..a4b7a9136 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -1,12 +1,13 @@ -from typing import Callable, Optional from collections import OrderedDict +from typing import Callable, Optional from rx import operators as ops -from rx.core import Observable, GroupedObservable +from rx.core import GroupedObservable, Observable from rx.core.typing import Mapper -from rx.subject import Subject -from rx.disposable import CompositeDisposable, RefCountDisposable, SingleAssignmentDisposable +from rx.disposable import (CompositeDisposable, RefCountDisposable, + SingleAssignmentDisposable) from rx.internal.basic import identity +from rx.subject import Subject def _group_by_until(key_mapper: Mapper, diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index 80a90ece1..fa4395ac5 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -1,11 +1,12 @@ import logging -from typing import Callable, Any from collections import OrderedDict +from typing import Any, Callable from rx import operators as ops from rx.core import Observable +from rx.disposable import (CompositeDisposable, RefCountDisposable, + SingleAssignmentDisposable) from rx.internal.utils import add_ref -from rx.disposable import SingleAssignmentDisposable, RefCountDisposable, CompositeDisposable from rx.subject import Subject log = logging.getLogger("Rx") diff --git a/rx/core/operators/isempty.py b/rx/core/operators/isempty.py index 9a2f4a575..7c9324276 100644 --- a/rx/core/operators/isempty.py +++ b/rx/core/operators/isempty.py @@ -1,9 +1,10 @@ -from typing import Callable +from typing import Any, Callable + from rx import operators as ops from rx.core import Observable, pipe -def _is_empty() -> Callable[[Observable], Observable]: +def _is_empty() -> Callable[[Observable[Any]], Observable[bool]]: """Determines whether an observable sequence is empty. Returns: @@ -11,7 +12,7 @@ def _is_empty() -> Callable[[Observable], Observable]: determining whether the source sequence is empty. """ - return pipe( - ops.some(), - ops.map(lambda b: not b) - ) + return pipe(ops.some(), ops.map(lambda b: not b)) + + +__all__ = ["_is_empty"] diff --git a/rx/core/operators/join.py b/rx/core/operators/join.py index b5818b89b..772816977 100644 --- a/rx/core/operators/join.py +++ b/rx/core/operators/join.py @@ -1,10 +1,10 @@ -from typing import Callable, Any from collections import OrderedDict +from typing import Any, Callable -from rx.operators import take from rx.core import Observable +from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal import noop -from rx.disposable import SingleAssignmentDisposable, CompositeDisposable +from rx.operators import take def _join(right: Observable, diff --git a/rx/core/operators/last.py b/rx/core/operators/last.py index b007dbb4f..4d07aa908 100644 --- a/rx/core/operators/last.py +++ b/rx/core/operators/last.py @@ -1,13 +1,16 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar + from rx import operators from rx.core import Observable from rx.core.typing import Predicate from .lastordefault import last_or_default_async +_T = TypeVar("_T") + -def _last(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: - def last(source: Observable) -> Observable: +def _last(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: + def last(source: Observable[_T]) -> Observable[_T]: """Partially applied last operator. Returns the last element of an observable sequence that @@ -27,10 +30,11 @@ def last(source: Observable) -> Observable: """ if predicate: - return source.pipe( - operators.filter(predicate), - operators.last() - ) + return source.pipe(operators.filter(predicate), operators.last()) return last_or_default_async(source, False) + return last + + +__all__ = ["_last"] diff --git a/rx/core/operators/lastordefault.py b/rx/core/operators/lastordefault.py index 884a28dbf..138e91948 100644 --- a/rx/core/operators/lastordefault.py +++ b/rx/core/operators/lastordefault.py @@ -1,21 +1,20 @@ -from typing import Callable, Any, Optional +from typing import Any, Callable, Optional, TypeVar -from rx.core import Observable -from rx.core.typing import Predicate -from rx.internal.exceptions import SequenceContainsNoElementsError from rx import operators as ops +from rx.core import Observable, abc, typing +from rx.internal.exceptions import SequenceContainsNoElementsError +_T = TypeVar("_T") -def last_or_default_async(source: Observable, - has_default: bool = False, - default_value: Any = None - ) -> Observable: - def subscribe(observer, scheduler=None): +def last_or_default_async( + source: Observable[_T], has_default: bool = False, default_value: Any = None +) -> Observable[_T]: + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): value = [default_value] seen_value = [False] - def on_next(x): + def on_next(x: _T) -> None: value[0] = x seen_value[0] = True @@ -27,14 +26,14 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) - return Observable(subscribe) + return Observable(subscribe) -def _last_or_default(predicate: Optional[Predicate] = None, - default_value: Any = None - ) -> Callable[[Observable], Observable]: - def last_or_default(source: Observable) -> Observable: +def _last_or_default( + predicate: Optional[typing.Predicate[_T]] = None, default_value: Any = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def last_or_default(source: Observable[_T]) -> Observable[_T]: """Return last or default element. Examples: @@ -55,4 +54,5 @@ def last_or_default(source: Observable) -> Observable: ) return last_or_default_async(source, True, default_value) + return last_or_default diff --git a/rx/core/operators/map.py b/rx/core/operators/map.py index ef184b46a..357bc7c52 100644 --- a/rx/core/operators/map.py +++ b/rx/core/operators/map.py @@ -1,19 +1,20 @@ -from typing import Callable, Any, Optional +from typing import Any, Callable, Optional, TypeVar +from rx import operators as ops +from rx.core import Observable, abc, pipe +from rx.core.typing import Mapper, MapperIndexed from rx.internal.basic import identity from rx.internal.utils import infinite -from rx import operators as ops -from rx.core import Observable, pipe -from rx.core.typing import Mapper, MapperIndexed, Observer, Disposable, Scheduler - +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") # pylint: disable=redefined-builtin -def _map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def _map(mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: _mapper = mapper or identity - def map(source: Observable) -> Observable: + def map(source: Observable[_T1]) -> Observable[_T2]: """Partially applied map operator. Project each element of an observable sequence into a new form @@ -31,7 +32,7 @@ def map(source: Observable) -> Observable: of the source. """ - def subscribe(obv: Observer, scheduler: Scheduler = None) -> Disposable: + def subscribe(obv: abc.ObserverBase[_T2], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: def on_next(value: Any) -> None: try: result = _mapper(value) @@ -41,18 +42,21 @@ def on_next(value: Any) -> None: obv.on_next(result) return source.subscribe_(on_next, obv.on_error, obv.on_completed, scheduler) + return Observable(subscribe) - return map + return map -def _map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable[[Observable], Observable]: +def _map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: def _identity(value: Any, _: int) -> Any: return value _mapper_indexed = mapper_indexed or _identity - return pipe( - ops.zip_with_iterable(infinite()), - ops.starmap_indexed(_mapper_indexed) - ) + return pipe(ops.zip_with_iterable(infinite()), ops.starmap_indexed(_mapper_indexed)) + + +__all__ = ["_map", "_map_indexed"] diff --git a/rx/core/operators/materialize.py b/rx/core/operators/materialize.py index 1b0313842..fecb8dc82 100644 --- a/rx/core/operators/materialize.py +++ b/rx/core/operators/materialize.py @@ -1,6 +1,7 @@ from typing import Callable + from rx.core import Observable -from rx.core.notification import OnNext, OnError, OnCompleted +from rx.core.notification import OnCompleted, OnError, OnNext def _materialize() -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/maxby.py b/rx/core/operators/maxby.py index 023f8d3ed..e066cfa98 100644 --- a/rx/core/operators/maxby.py +++ b/rx/core/operators/maxby.py @@ -1,4 +1,5 @@ from typing import Callable, Optional + from rx.core import Observable from rx.core.typing import Mapper, SubComparer from rx.internal.basic import default_sub_comparer diff --git a/rx/core/operators/min.py b/rx/core/operators/min.py index dd104f1b5..b1e296263 100644 --- a/rx/core/operators/min.py +++ b/rx/core/operators/min.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Sequence, Any +from typing import Any, Callable, Optional, Sequence from rx import operators as ops from rx.core import Observable, pipe diff --git a/rx/core/operators/multicast.py b/rx/core/operators/multicast.py index 6400da238..5edbaf102 100644 --- a/rx/core/operators/multicast.py +++ b/rx/core/operators/multicast.py @@ -1,12 +1,12 @@ -from typing import Union, Callable, Optional +from typing import Callable, Optional, Union -from rx.core import Observable, ConnectableObservable -from rx.core.typing import Subject, Mapper, Scheduler +from rx.core import ConnectableObservable, Observable, abc +from rx.core.typing import Mapper from rx.disposable import CompositeDisposable -def _multicast(subject: Optional[Subject] = None, - subject_factory: Optional[Callable[[Optional[Scheduler]], Subject]] = None, +def _multicast(subject: Optional[abc.SubjectBase] = None, + subject_factory: Optional[Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase]] = None, mapper: Optional[Callable[[ConnectableObservable], Observable]] = None ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: """Multicasts the source sequence notifications through an diff --git a/rx/core/operators/observeon.py b/rx/core/operators/observeon.py index 09b0441b3..93fa19b43 100644 --- a/rx/core/operators/observeon.py +++ b/rx/core/operators/observeon.py @@ -1,11 +1,10 @@ from typing import Callable -from rx.core import Observable -from rx.core.typing import Scheduler +from rx.core import Observable, abc from rx.core.observer import ObserveOnObserver -def _observe_on(scheduler: Scheduler) -> Callable[[Observable], Observable]: +def _observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: def observe_on(source: Observable) -> Observable: """Wraps the source sequence in order to run its observer callbacks on the specified scheduler. @@ -23,9 +22,10 @@ def observe_on(source: Observable) -> Observable: Returns the source sequence whose observations happen on the specified scheduler. """ + def subscribe(observer, subscribe_scheduler=None): - return source.subscribe(ObserveOnObserver(scheduler, observer), - scheduler=subscribe_scheduler) + return source.subscribe(ObserveOnObserver(scheduler, observer), scheduler=subscribe_scheduler) return Observable(subscribe) + return observe_on diff --git a/rx/core/operators/partition.py b/rx/core/operators/partition.py index c4b577d22..dc527013c 100644 --- a/rx/core/operators/partition.py +++ b/rx/core/operators/partition.py @@ -1,4 +1,4 @@ -from typing import List, Callable +from typing import Callable, List from rx import operators as ops from rx.core import Observable diff --git a/rx/core/operators/publish.py b/rx/core/operators/publish.py index 293f39351..3cee93efa 100644 --- a/rx/core/operators/publish.py +++ b/rx/core/operators/publish.py @@ -1,7 +1,7 @@ from typing import Callable, Optional from rx import operators as ops -from rx.core import Observable, ConnectableObservable, pipe +from rx.core import ConnectableObservable, Observable, pipe from rx.core.typing import Mapper from rx.subject import Subject diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index 774d60d3f..e526dc1d7 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -2,8 +2,8 @@ from rx import operators as ops from rx.core import Observable -from rx.subject import BehaviorSubject from rx.core.typing import Mapper +from rx.subject import BehaviorSubject def _publish_value(initial_value: Any, mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 112514e69..9fa1c11af 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -1,12 +1,17 @@ -from typing import Any, Callable +from typing import Any, Callable, TypeVar from rx import operators as ops -from rx.internal.utils import NotSet from rx.core import Observable, pipe from rx.core.typing import Accumulator +from rx.internal.utils import NotSet + +_T = TypeVar("_T") +_TState = TypeVar("_TState") -def _reduce(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable], Observable]: +def _reduce( + accumulator: Accumulator[_TState, _T], seed: Any = NotSet +) -> Callable[[Observable[_T]], Observable[_TState]]: """Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified seed value is used as the initial @@ -34,3 +39,6 @@ def _reduce(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observabl return pipe(scanner, ops.last_or_default(default_value=seed)) return pipe(ops.scan(accumulator), ops.last()) + + +__all__ = ["_reduce"] diff --git a/rx/core/operators/replay.py b/rx/core/operators/replay.py index 62c94455d..e6eccbb42 100644 --- a/rx/core/operators/replay.py +++ b/rx/core/operators/replay.py @@ -1,16 +1,17 @@ -from typing import Union, Callable, Optional +from typing import Callable, Optional, Union from rx import operators as ops -from rx.core import Observable, ConnectableObservable, typing -from rx.core.typing import Scheduler, Mapper +from rx.core import ConnectableObservable, Observable, abc, typing +from rx.core.typing import Mapper from rx.subject import ReplaySubject -def _replay(mapper: Optional[Mapper] = None, - buffer_size: Optional[int] = None, - window: Optional[typing.RelativeTime] = None, - scheduler: Optional[Scheduler] = None - ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: +def _replay( + mapper: Optional[Mapper] = None, + buffer_size: Optional[int] = None, + window: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence replaying notifications @@ -43,8 +44,10 @@ def _replay(mapper: Optional[Mapper] = None, """ if mapper: + def subject_factory(scheduler): return ReplaySubject(buffer_size, window, scheduler) + return ops.multicast(subject_factory=subject_factory, mapper=mapper) return ops.multicast(ReplaySubject(buffer_size, window, scheduler)) diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index e39b95f47..d91a3c4a4 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -1,7 +1,7 @@ from typing import Callable, Optional, Union import rx -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable @@ -28,15 +28,15 @@ def on_completed(): return CompositeDisposable( source.subscribe_(on_next, observer.on_error, on_completed, scheduler), - sampler.subscribe_(sample_subscribe, observer.on_error, sample_subscribe, scheduler) + sampler.subscribe_(sample_subscribe, observer.on_error, sample_subscribe, scheduler), ) - return Observable(subscribe) + return Observable(subscribe) -def _sample(sampler: Union[typing.RelativeTime, Observable], - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _sample( + sampler: Union[typing.RelativeTime, Observable], scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def sample(source: Observable) -> Observable: """Samples the observable sequence at each interval. @@ -50,7 +50,7 @@ def sample(source: Observable) -> Observable: Sampled observable sequence. """ - if isinstance(sampler, typing.Observable): + if isinstance(sampler, abc.ObservableBase): return sample_observable(source, sampler) else: return sample_observable(source, rx.interval(sampler, scheduler=scheduler)) diff --git a/rx/core/operators/scan.py b/rx/core/operators/scan.py index f4fe05ef2..feced7390 100644 --- a/rx/core/operators/scan.py +++ b/rx/core/operators/scan.py @@ -1,14 +1,21 @@ -from typing import Any, Callable +from typing import Callable, TypeVar, Union, Type, cast -from rx import defer, operators as ops -from rx.internal.utils import NotSet -from rx.core import Observable +from rx import defer +from rx import operators as ops +from rx.core import Observable, abc from rx.core.typing import Accumulator +from rx.internal.utils import NotSet + +_T = TypeVar("_T") +_TState = TypeVar("_TState") -def _scan(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable], Observable]: + +def _scan( + accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet +) -> Callable[[Observable[_T]], Observable[_TState]]: has_seed = seed is not NotSet - def scan(source: Observable) -> Observable: + def scan(source: Observable[_T]) -> Observable[_TState]: """Partially applied scan operator. Applies an accumulator function over an observable sequence and @@ -24,11 +31,11 @@ def scan(source: Observable) -> Observable: An observable sequence containing the accumulated values. """ - def factory(scheduler): + def factory(scheduler: abc.SchedulerBase) -> Observable[_TState]: has_accumulation = False - accumulation = None + accumulation: _TState = cast(_TState, None) - def projection(x): + def projection(x: _T) -> _TState: nonlocal has_accumulation nonlocal accumulation @@ -39,6 +46,12 @@ def projection(x): has_accumulation = True return accumulation + return source.pipe(ops.map(projection)) + return defer(factory) + return scan + + +__all__ = ["_scan"] diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index 0612135ee..c1f8fd7a6 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -1,5 +1,5 @@ -from typing import Any, Callable, Optional import collections +from typing import Any, Callable, Optional import rx from rx.core import Observable diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index cef4da37b..e96e0d1eb 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -1,8 +1,8 @@ -from typing import Optional, Callable +from typing import Callable, Optional from rx import operators as ops from rx.core import Observable, pipe -from rx.core.typing import Predicate, Any +from rx.core.typing import Any, Predicate from rx.internal.exceptions import SequenceContainsNoElementsError diff --git a/rx/core/operators/skiplast.py b/rx/core/operators/skiplast.py index bda96f40b..14d200ea1 100644 --- a/rx/core/operators/skiplast.py +++ b/rx/core/operators/skiplast.py @@ -1,9 +1,12 @@ -from typing import Callable -from rx.core import Observable +from typing import Callable, List, Optional, TypeVar +from rx.core import Observable, abc -def _skip_last(count: int) -> Callable[[Observable], Observable]: - def skip_last(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def _skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: + def skip_last(source: Observable[_T]) -> Observable[_T]: """Bypasses a specified number of elements at the end of an observable sequence. @@ -21,10 +24,10 @@ def skip_last(source: Observable) -> Observable: elements except for the bypassed ones at the end. """ - def subscribe(observer, scheduler=None): - q = [] + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + q: List[_T] = [] - def on_next(value): + def on_next(value: _T) -> None: front = None with source.lock: q.append(value) @@ -35,5 +38,10 @@ def on_next(value): observer.on_next(front) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return Observable(subscribe) + return skip_last + + +__all__ = ["_skip_last"] diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index a81e7cc3e..a154a84e7 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -1,11 +1,12 @@ from typing import Callable, Optional -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler -def _skip_last_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _skip_last_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Skips elements for the specified duration from the end of the observable source sequence. @@ -50,5 +51,7 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler_) + return Observable(subscribe) + return skip_last_with_time diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index 5266e8a42..ca87d8d3b 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -1,13 +1,14 @@ -from asyncio import Future -from typing import cast, Callable, Union +from typing import Any, Callable, Optional, TypeVar, Union, cast from rx import from_future -from rx.core import Observable +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import is_future +_T = TypeVar("_T") -def _skip_until(other: Union[Observable, Future]) -> Callable[[Observable], Observable]: + +def _skip_until(other: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after the other observable sequence produces a value. @@ -22,19 +23,19 @@ def _skip_until(other: Union[Observable, Future]) -> Callable[[Observable], Obse """ if is_future(other): - obs = from_future(cast(Future, other)) + obs: Observable[Any] = from_future(cast(typing.Future, other)) else: - obs = cast(Observable, other) + obs = cast(Observable[_T], other) - def skip_until(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def skip_until(source: Observable[_T]) -> Observable[_T]: + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): is_open = [False] - def on_next(left): + def on_next(left: _T) -> None: if is_open[0]: observer.on_next(left) - def on_completed(): + def on_completed() -> None: if is_open[0]: observer.on_completed() @@ -44,7 +45,7 @@ def on_completed(): right_subscription = SingleAssignmentDisposable() subscriptions.add(right_subscription) - def on_next2(x): + def on_next2(x: Any) -> None: is_open[0] = True right_subscription.dispose() @@ -54,5 +55,10 @@ def on_completed2(): right_subscription.disposable = obs.subscribe_(on_next2, observer.on_error, on_completed2, scheduler) return subscriptions + return Observable(subscribe) + return skip_until + + +__all__ = ["_skip_until"] diff --git a/rx/core/operators/skipuntilwithtime.py b/rx/core/operators/skipuntilwithtime.py index 97894dc38..8aeaf85d0 100644 --- a/rx/core/operators/skipuntilwithtime.py +++ b/rx/core/operators/skipuntilwithtime.py @@ -1,14 +1,17 @@ from datetime import datetime -from typing import Callable, Optional +from typing import Any, Callable, Optional, TypeVar -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _skip_until_with_time(start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: - def skip_until_with_time(source: Observable) -> Observable: + +def _skip_until_with_time( + start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def skip_until_with_time(source: Observable[_T]) -> Observable[_T]: """Skips elements from the observable source sequence until the specified start time. @@ -31,23 +34,30 @@ def skip_until_with_time(source: Observable) -> Observable: """ if isinstance(start_time, datetime): - scheduler_method = 'schedule_absolute' + scheduler_method = "schedule_absolute" else: - scheduler_method = 'schedule_relative' + scheduler_method = "schedule_relative" - def subscribe(observer, scheduler_=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() open = [False] - def on_next(x): + def on_next(x: _T) -> None: if open[0]: observer.on_next(x) + subscription = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler_) - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any): open[0] = True + disp = getattr(_scheduler, scheduler_method)(start_time, action) return CompositeDisposable(disp, subscription) + return Observable(subscribe) + return skip_until_with_time + + +__all__ = ["_skip_until_with_time"] diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index a68accd3c..a0f20905e 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -1,11 +1,13 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, typing, pipe +from rx.core import Observable, abc, pipe, typing +_T = TypeVar("_T") -def _skip_while(predicate: typing.Predicate) -> Callable[[Observable], Observable]: - def skip_while(source: Observable) -> Observable: + +def _skip_while(predicate: typing.Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: + def skip_while(source: Observable[_T]) -> Observable[_T]: """Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. The element's index is used in the logic of the @@ -22,10 +24,11 @@ def skip_while(source: Observable) -> Observable: input sequence starting at the first element in the linear series that does not pass the test specified by predicate. """ - def subscribe(observer, scheduler=None): + + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): running = False - def on_next(value): + def on_next(value: _T): nonlocal running if not running: @@ -39,13 +42,18 @@ def on_next(value): observer.on_next(value) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return Observable(subscribe) + return skip_while -def _skip_while_indexed(predicate: typing.PredicateIndexed) -> Callable[[Observable], Observable]: +def _skip_while_indexed(predicate: typing.PredicateIndexed[_T]) -> Callable[[Observable[_T]], Observable[_T]]: return pipe( ops.map_indexed(lambda x, i: (x, i)), ops.skip_while(lambda x: predicate(*x)), - ops.map(lambda x: x[0]) + ops.map(lambda x: x[0]), ) + + +__all__ = ["_skip_while", "_skip_while_indexed"] diff --git a/rx/core/operators/skipwithtime.py b/rx/core/operators/skipwithtime.py index b0526f136..d227f8eda 100644 --- a/rx/core/operators/skipwithtime.py +++ b/rx/core/operators/skipwithtime.py @@ -1,13 +1,16 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, Any -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _skip_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: - def skip_with_time(source: Observable) -> Observable: + +def _skip_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def skip_with_time(source: Observable[_T]) -> Observable[_T]: """Skips elements for the specified duration from the start of the observable source sequence. @@ -34,20 +37,25 @@ def skip_with_time(source: Observable) -> Observable: specified duration from the start of the source sequence. """ - def subscribe(observer, scheduler_=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() open = [False] - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any) -> None: open[0] = True t = _scheduler.schedule_relative(duration, action) - def on_next(x): + def on_next(x: _T): if open[0]: observer.on_next(x) d = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler_) return CompositeDisposable(t, d) + return Observable(subscribe) + return skip_with_time + + +__all__ = ["_skip_with_time"] diff --git a/rx/core/operators/slice.py b/rx/core/operators/slice.py index 0dd00a876..c929ccaa9 100644 --- a/rx/core/operators/slice.py +++ b/rx/core/operators/slice.py @@ -1,22 +1,23 @@ from sys import maxsize -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, Any, List from rx import operators as ops from rx.core import Observable +_T = TypeVar("_T") + # pylint: disable=redefined-builtin -def _slice(start: Optional[int] = None, - stop: Optional[int] = None, - step: Optional[int] = None - ) -> Callable[[Observable], Observable]: +def _slice( + start: Optional[int] = None, stop: Optional[int] = None, step: Optional[int] = None +) -> Callable[[Observable[_T]], Observable[_T]]: _start: int = 0 if start is None else start _stop: int = maxsize if stop is None else stop _step: int = 1 if step is None else step - pipeline = [] + pipeline: List[Callable[[Observable[Any]], Observable[Any]]] = [] - def slice(source: Observable) -> Observable: + def slice(source: Observable[_T]) -> Observable[_T]: """The partially applied slice operator. Slices the given observable. It is basically a wrapper around the operators @@ -64,7 +65,11 @@ def slice(source: Observable) -> Observable: pipeline.append(ops.filter_indexed(lambda x, i: i % _step == 0)) elif _step < 0: # Reversing events is not supported - raise TypeError('Negative step not supported.') + raise TypeError("Negative step not supported.") return source.pipe(*pipeline) + return slice + + +__all__ = ["_slice"] diff --git a/rx/core/operators/some.py b/rx/core/operators/some.py index 89234addd..38848a4ed 100644 --- a/rx/core/operators/some.py +++ b/rx/core/operators/some.py @@ -1,11 +1,14 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Predicate -def _some(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: - def some(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def _some(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[bool]]: + def some(source: Observable[_T]) -> Observable[bool]: """Partially applied operator. Determines whether some element of an observable sequence satisfies a @@ -23,14 +26,16 @@ def some(source: Observable) -> Observable: pass the test in the specified predicate if given, else if some items are in the sequence. """ - def subscribe(observer, scheduler=None): - def on_next(_): + + def subscribe(observer: abc.ObserverBase[bool], scheduler: Optional[abc.SchedulerBase] = None): + def on_next(_: _T): observer.on_next(True) observer.on_completed() def on_error(): observer.on_next(False) observer.on_completed() + return source.subscribe_(on_next, observer.on_error, on_error, scheduler) if predicate: @@ -40,4 +45,5 @@ def on_error(): ) return Observable(subscribe) + return some diff --git a/rx/core/operators/startswith.py b/rx/core/operators/startswith.py index 16970b64a..8249c142a 100644 --- a/rx/core/operators/startswith.py +++ b/rx/core/operators/startswith.py @@ -1,12 +1,13 @@ -from typing import Any, Callable +from typing import Callable, TypeVar import rx - from rx.core import Observable +_T = TypeVar("_T") + -def _start_with(*args: Any) -> Callable[[Observable], Observable]: - def start_with(source: Observable) -> Observable: +def _start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: + def start_with(source: Observable[_T]) -> Observable[_T]: """Partially applied start_with operator. Prepends a sequence of values to an observable sequence. @@ -20,4 +21,8 @@ def start_with(source: Observable) -> Observable: start = rx.from_iterable(args) sequence = [start, source] return rx.concat(*sequence) + return start_with + + +__all__ = ["_start_with"] diff --git a/rx/core/operators/statistics.py b/rx/core/operators/statistics.py index 0cc794f69..2ef2f017f 100644 --- a/rx/core/operators/statistics.py +++ b/rx/core/operators/statistics.py @@ -1,6 +1,7 @@ -from rx.core import Observable import math +from rx.core import Observable + def determine_median(sorted_list): if len(sorted_list) == 0: diff --git a/rx/core/operators/subscribeon.py b/rx/core/operators/subscribeon.py index 9ff524ede..419af02de 100644 --- a/rx/core/operators/subscribeon.py +++ b/rx/core/operators/subscribeon.py @@ -1,11 +1,13 @@ -from typing import Callable -from rx.core import Observable -from rx.core.typing import Scheduler -from rx.disposable import SingleAssignmentDisposable, SerialDisposable, ScheduledDisposable +from typing import Callable, TypeVar, Optional, Any +from rx.core import Observable, abc +from rx.disposable import ScheduledDisposable, SerialDisposable, SingleAssignmentDisposable -def _subscribe_on(scheduler: Scheduler) -> Callable[[Observable], Observable]: - def subscribe_on(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def _subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable[_T]], Observable[_T]]: + def subscribe_on(source: Observable[_T]) -> Observable[_T]: """Subscribe on the specified scheduler. Wrap the source sequence in order to run its subscription and @@ -25,16 +27,21 @@ def subscribe_on(source: Observable) -> Observable: The source sequence whose subscriptions and un-subscriptions happen on the specified scheduler. """ - def subscribe(observer, _=None): + + def subscribe(observer: abc.ObserverBase[_T], _: Optional[abc.SchedulerBase] = None): m = SingleAssignmentDisposable() d = SerialDisposable() d.disposable = m - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): d.disposable = ScheduledDisposable(scheduler, source.subscribe(observer)) m.disposable = scheduler.schedule(action) return d return Observable(subscribe) + return subscribe_on + + +__all__ = ["_subscribe_on"] diff --git a/rx/core/operators/sum.py b/rx/core/operators/sum.py index a9c566e16..84059211f 100644 --- a/rx/core/operators/sum.py +++ b/rx/core/operators/sum.py @@ -1,15 +1,18 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Mapper +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") -def _sum(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: + +def _sum(key_mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: if key_mapper: - return pipe( - ops.map(key_mapper), - ops.sum() - ) + return pipe(ops.map(key_mapper), ops.sum()) return ops.reduce(seed=0, accumulator=lambda prev, curr: prev + curr) + + +__all__ = ["_sum"] diff --git a/rx/core/operators/switchlatest.py b/rx/core/operators/switchlatest.py index 97a7f8e40..7c2d72fe6 100644 --- a/rx/core/operators/switchlatest.py +++ b/rx/core/operators/switchlatest.py @@ -1,9 +1,10 @@ from asyncio import Future -from typing import cast, Any, Callable, Union +from typing import Any, Callable, Union, cast from rx import from_future from rx.core import Observable -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) from rx.internal.utils import is_future diff --git a/rx/core/operators/takelast.py b/rx/core/operators/takelast.py index 5333cc40a..1cb837dd0 100644 --- a/rx/core/operators/takelast.py +++ b/rx/core/operators/takelast.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/takelastbuffer.py b/rx/core/operators/takelastbuffer.py index d7de51d13..4620d040c 100644 --- a/rx/core/operators/takelastbuffer.py +++ b/rx/core/operators/takelastbuffer.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/takelastwithtime.py b/rx/core/operators/takelastwithtime.py index 071f8bd2f..583ed3b44 100644 --- a/rx/core/operators/takelastwithtime.py +++ b/rx/core/operators/takelastwithtime.py @@ -1,10 +1,12 @@ from typing import Callable, Optional -from rx.core import Observable, typing + +from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler -def _take_last_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _take_last_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def take_last_with_time(source: Observable) -> Observable: """Returns elements within the specified duration from the end of the observable source sequence. @@ -26,6 +28,7 @@ def take_last_with_time(source: Observable) -> Observable: An observable sequence with the elements taken during the specified duration from the end of the source sequence. """ + def subscribe(observer, scheduler_=None): nonlocal duration @@ -49,5 +52,7 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler_) + return Observable(subscribe) + return take_last_with_time diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index 4a3e736af..e63080a39 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -1,10 +1,10 @@ from asyncio import Future -from typing import cast, Callable, Union +from typing import Callable, Union, cast from rx import from_future -from rx.internal import noop from rx.core import Observable from rx.disposable import CompositeDisposable +from rx.internal import noop from rx.internal.utils import is_future diff --git a/rx/core/operators/takeuntilwithtime.py b/rx/core/operators/takeuntilwithtime.py index 9e2ffd8ab..67e218ab1 100644 --- a/rx/core/operators/takeuntilwithtime.py +++ b/rx/core/operators/takeuntilwithtime.py @@ -1,13 +1,14 @@ -from typing import Callable, Optional from datetime import datetime +from typing import Callable, Optional -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler -def _take_until_with_time(end_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _take_until_with_time( + end_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def take_until_with_time(source: Observable) -> Observable: """Takes elements for the specified duration until the specified end time, using the specified scheduler to run timers. @@ -36,5 +37,7 @@ def action(scheduler, state): task = scheduler_method(end_time, action) return CompositeDisposable(task, source.subscribe(observer, scheduler=scheduler_)) + return Observable(subscribe) + return take_until_with_time diff --git a/rx/core/operators/takewithtime.py b/rx/core/operators/takewithtime.py index b30e38251..0cd44154d 100644 --- a/rx/core/operators/takewithtime.py +++ b/rx/core/operators/takewithtime.py @@ -1,12 +1,13 @@ from typing import Callable, Optional -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler -def _take_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _take_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def take_with_time(source: Observable) -> Observable: """Takes elements for the specified duration from the start of the observable source sequence. @@ -36,5 +37,7 @@ def action(scheduler, state): disp = _scheduler.schedule_relative(duration, action) return CompositeDisposable(disp, source.subscribe(observer, scheduler=scheduler_)) + return Observable(subscribe) + return take_with_time diff --git a/rx/core/operators/throttlefirst.py b/rx/core/operators/throttlefirst.py index 99b65df07..308fca02c 100644 --- a/rx/core/operators/throttlefirst.py +++ b/rx/core/operators/throttlefirst.py @@ -1,11 +1,12 @@ from typing import Callable, Optional -from rx.core import Observable, typing +from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler -def _throttle_first(window_duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _throttle_first( + window_duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def throttle_first(source: Observable) -> Observable: """Returns an observable that emits only the first item emitted by the source Observable during sequential time windows of a @@ -17,12 +18,13 @@ def throttle_first(source: Observable) -> Observable: Returns: An Observable that performs the throttle operation. """ + def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() duration = _scheduler.to_timedelta(window_duration or 0.0) if duration <= _scheduler.to_timedelta(0): - raise ValueError('window_duration cannot be less or equal zero.') + raise ValueError("window_duration cannot be less or equal zero.") last_on_next = [0] def on_next(x): @@ -37,5 +39,7 @@ def on_next(x): observer.on_next(x) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler=_scheduler) + return Observable(subscribe) + return throttle_first diff --git a/rx/core/operators/timeinterval.py b/rx/core/operators/timeinterval.py index cfd67b5d4..c17dbd9d7 100644 --- a/rx/core/operators/timeinterval.py +++ b/rx/core/operators/timeinterval.py @@ -1,9 +1,8 @@ -from typing import Callable, NamedTuple, Any, Optional from datetime import timedelta - +from typing import Any, Callable, NamedTuple, Optional from rx import operators as ops -from rx.core import Observable, typing +from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler @@ -12,7 +11,7 @@ class TimeInterval(NamedTuple): interval: timedelta -def _time_interval(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def _time_interval(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: def time_interval(source: Observable) -> Observable: """Records the time interval between consecutive values in an observable sequence. @@ -37,5 +36,7 @@ def mapper(value): return TimeInterval(value=value, interval=span) return source.pipe(ops.map(mapper)).subscribe(observer, scheduler_) + return Observable(subscribe) + return time_interval diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index 14f057c4b..616345e3a 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -1,18 +1,20 @@ from asyncio import Future from datetime import datetime -from typing import cast, Callable, Optional, Union +from typing import Callable, Optional, Union, cast from rx import from_future, throw -from rx.core import Observable, typing -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable -from rx.scheduler import TimeoutScheduler +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) from rx.internal.utils import is_future +from rx.scheduler import TimeoutScheduler -def _timeout(duetime: typing.AbsoluteTime, - other: Optional[Union[Observable, Future]] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _timeout( + duetime: typing.AbsoluteTime, + other: Optional[Union[Observable, Future]] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: other = other or throw(Exception("Timeout")) if is_future(other): @@ -34,6 +36,7 @@ def timeout(source: Observable) -> Observable: An obserable sequence switching to the other sequence in case of a timeout. """ + def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() @@ -54,7 +57,7 @@ def create_timer(): my_id = _id[0] def action(scheduler, state=None): - switched[0] = (_id[0] == my_id) + switched[0] = _id[0] == my_id timer_wins = switched[0] if timer_wins: subscription.disposable = obs.subscribe(observer, scheduler=scheduler) @@ -84,5 +87,7 @@ def on_completed(): original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler_) return CompositeDisposable(subscription, timer) + return Observable(subscribe) + return timeout diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index e6b535982..20ec6e38e 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -1,8 +1,9 @@ -from typing import Callable, Optional, Any +from typing import Any, Callable, Optional import rx from rx.core import Observable -from rx.disposable import CompositeDisposable, SingleAssignmentDisposable, SerialDisposable +from rx.disposable import (CompositeDisposable, SerialDisposable, + SingleAssignmentDisposable) def _timeout_with_mapper(first_timeout: Optional[Observable] = None, diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index 68cb85b40..d388fc9c8 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -1,10 +1,9 @@ -from typing import Callable, NamedTuple, Any, Optional from datetime import datetime +from typing import Any, Callable, NamedTuple, Optional -from rx import defer -from rx.core import Observable, typing +from rx import defer, operators +from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler -from rx import operators class Timestamp(NamedTuple): @@ -12,7 +11,7 @@ class Timestamp(NamedTuple): timestamp: datetime -def _timestamp(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def _timestamp(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: def timestamp(source: Observable) -> Observable: """Records the timestamp for each value in an observable sequence. @@ -34,5 +33,7 @@ def factory(scheduler_=None): mapper = operators.map(lambda value: Timestamp(value=value, timestamp=_scheduler.now)) return source.pipe(mapper) + return defer(factory) + return timestamp diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index 17d456a70..66ab39624 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -1,12 +1,10 @@ -from typing import Callable, Any, Optional +from typing import Any, Callable, Optional -from rx.core import typing, Observable +from rx.core import Observable, abc from rx.core.typing import Mapper -def _to_dict(key_mapper: Mapper, - element_mapper: Optional[Mapper] = None - ) -> Callable[[Observable], Observable]: +def _to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: def to_dict(source: Observable) -> Observable: """Converts the observable sequence to a Map if it exists. @@ -18,7 +16,7 @@ def to_dict(source: Observable) -> Observable: containing the values from the observable sequence. """ - def subscribe(observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None) -> typing.Disposable: + def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: m = dict() def on_next(x: Any) -> None: @@ -45,6 +43,7 @@ def on_completed() -> None: observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return Observable(subscribe) - return to_dict + return to_dict diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index f0f1d4355..acae08f3e 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -1,13 +1,15 @@ -from typing import Callable, Optional from asyncio import Future +from typing import Callable, Optional -from .. import typing from rx.core import Observable from rx.internal.exceptions import SequenceContainsNoElementsError +from .. import abc -def _to_future(future_ctor: Optional[Callable[[], Future]] = None, - scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Future]: + +def _to_future( + future_ctor: Optional[Callable[[], Future]] = None, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Future]: future_ctor = future_ctor or Future future = future_ctor() @@ -54,4 +56,5 @@ def on_completed(): future.add_done_callback(lambda _: dis.dispose()) return future + return to_future diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index a7b811ebd..575f547fb 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index 28870a640..68dc7b529 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -1,14 +1,13 @@ from typing import List, Optional -from rx.core import Observable -from rx.core.typing import Scheduler, RelativeTime +from rx.core import Observable, abc +from rx.core.typing import RelativeTime from rx.scheduler import NewThreadScheduler new_thread_scheduler = NewThreadScheduler() -def _to_marbles(scheduler: Optional[Scheduler] = None, timespan: RelativeTime = 0.1): - +def _to_marbles(scheduler: Optional[abc.SchedulerBase] = None, timespan: RelativeTime = 0.1): def to_marbles(source: Observable) -> Observable: """Convert an observable sequence into a marble diagram string. @@ -55,13 +54,14 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, on_error, on_completed) + return Observable(subscribe) + return to_marbles def stringify(value): - """Utility for stringifying an event. - """ + """Utility for stringifying an event.""" string = str(value) if len(string) > 1: string = "(%s)" % string diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index d9bbcec0e..57d6d973e 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -1,4 +1,5 @@ from typing import Callable + from rx.core import Observable diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index ada7994a7..86e69a4d6 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -1,11 +1,11 @@ -from asyncio import Future -from typing import cast, Callable, Union import itertools +from asyncio import Future +from typing import Callable, Union, cast import rx from rx.core import Observable from rx.core.typing import Predicate -from rx.internal.utils import is_future, infinite +from rx.internal.utils import infinite, is_future def _while_do(condition: Predicate) -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/window.py b/rx/core/operators/window.py index d7b610435..125b476df 100644 --- a/rx/core/operators/window.py +++ b/rx/core/operators/window.py @@ -1,13 +1,14 @@ import logging -from typing import Callable, Any +from typing import Any, Callable from rx import empty +from rx import operators as ops from rx.core import Observable +from rx.disposable import (CompositeDisposable, RefCountDisposable, + SerialDisposable, SingleAssignmentDisposable) from rx.internal import noop from rx.internal.utils import add_ref -from rx.disposable import SingleAssignmentDisposable, SerialDisposable, CompositeDisposable, RefCountDisposable from rx.subject import Subject -from rx import operators as ops log = logging.getLogger("Rx") diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index bdc121495..8ca515871 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -1,10 +1,10 @@ -from typing import Callable, Optional import logging +from typing import Callable, Optional from rx.core import Observable -from rx.internal.utils import add_ref -from rx.disposable import SingleAssignmentDisposable, RefCountDisposable +from rx.disposable import RefCountDisposable, SingleAssignmentDisposable from rx.internal.exceptions import ArgumentOutOfRangeException +from rx.internal.utils import add_ref from rx.subject import Subject log = logging.getLogger("Rx") diff --git a/rx/core/operators/windowwithtime.py b/rx/core/operators/windowwithtime.py index 839662b29..cfd2b9b50 100644 --- a/rx/core/operators/windowwithtime.py +++ b/rx/core/operators/windowwithtime.py @@ -1,16 +1,20 @@ -from typing import Callable, Optional from datetime import timedelta +from typing import Callable, Optional -from rx.core import Observable, typing -from rx.scheduler import TimeoutScheduler +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, RefCountDisposable, + SerialDisposable, SingleAssignmentDisposable) from rx.internal.constants import DELTA_ZERO from rx.internal.utils import add_ref -from rx.disposable import SingleAssignmentDisposable, CompositeDisposable, RefCountDisposable, SerialDisposable +from rx.scheduler import TimeoutScheduler from rx.subject import Subject -def _window_with_time(timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def _window_with_time( + timespan: typing.RelativeTime, + timeshift: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: if timeshift is None: timeshift = timespan @@ -69,6 +73,7 @@ def action(scheduler, state=None): s.on_completed() create_timer() + m.disposable = _scheduler.schedule_relative(ts, action) q.append(Subject()) @@ -93,5 +98,7 @@ def on_completed(): group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler_)) return ref_count_disposable + return Observable(subscribe) + return window_with_time diff --git a/rx/core/operators/windowwithtimeorcount.py b/rx/core/operators/windowwithtimeorcount.py index 8df5fad39..39c7c28bc 100644 --- a/rx/core/operators/windowwithtimeorcount.py +++ b/rx/core/operators/windowwithtimeorcount.py @@ -1,14 +1,16 @@ from typing import Callable, Optional -from rx.core import Observable, typing -from rx.scheduler import TimeoutScheduler +from rx.core import Observable, abc, typing +from rx.disposable import (CompositeDisposable, RefCountDisposable, + SerialDisposable, SingleAssignmentDisposable) from rx.internal.utils import add_ref -from rx.disposable import SingleAssignmentDisposable, CompositeDisposable, RefCountDisposable, SerialDisposable +from rx.scheduler import TimeoutScheduler from rx.subject import Subject -def _window_with_time_or_count(timespan: typing.RelativeTime, count: int, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def _window_with_time_or_count( + timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: def window_with_time_or_count(source: Observable) -> Observable: def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() @@ -70,5 +72,7 @@ def on_completed(): group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler_)) return ref_count_disposable + return Observable(subscribe) + return window_with_time_or_count diff --git a/rx/core/operators/withlatestfrom.py b/rx/core/operators/withlatestfrom.py index 95c4db7b7..6ac80c8e3 100644 --- a/rx/core/operators/withlatestfrom.py +++ b/rx/core/operators/withlatestfrom.py @@ -4,7 +4,6 @@ from rx.core import Observable - def _with_latest_from(*sources: Observable) -> Callable[[Observable], Observable]: """With latest from operator. diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index 41e896c7a..3f58b16cc 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -3,6 +3,7 @@ import rx from rx.core import Observable + # pylint: disable=redefined-builtin def _zip(*args: Observable) -> Callable[[Observable], Observable]: def zip(source: Observable) -> Observable: diff --git a/rx/core/pipe.py b/rx/core/pipe.py index ed4ec5004..06e8208ee 100644 --- a/rx/core/pipe.py +++ b/rx/core/pipe.py @@ -1,17 +1,17 @@ -from typing import Callable, Any, TypeVar, overload from functools import reduce +from typing import Any, Callable, TypeVar, overload -A = TypeVar('A') -B = TypeVar('B') -C = TypeVar('C') -D = TypeVar('D') -E = TypeVar('E') -F = TypeVar('F') -G = TypeVar('G') +A = TypeVar("A") +B = TypeVar("B") +C = TypeVar("C") +D = TypeVar("D") +E = TypeVar("E") +F = TypeVar("F") +G = TypeVar("G") @overload -def pipe(*operators: Callable[['Observable'], 'Observable']) -> Callable[['Observable'], 'Observable']: # type: ignore +def pipe(*operators: Callable[["Observable"], "Observable"]) -> Callable[["Observable"], "Observable"]: # type: ignore """Compose multiple operators left to right. Composes zero or more operators into a functional composition. The @@ -30,60 +30,57 @@ def pipe(*operators: Callable[['Observable'], 'Observable']) -> Callable[['Obser """ ... -@overload -def pipe() -> Callable[[A], A]: # pylint: disable=function-redefined - ... # pylint: disable=pointless-statement - @overload -def pipe(op1: Callable[[A], B]) -> Callable[[A], B]: # pylint: disable=function-redefined - ... # pylint: disable=pointless-statement +def pipe(__op1: Callable[[A], B]) -> Callable[[A], B]: # pylint: disable=function-redefined + ... @overload -def pipe(op1: Callable[[A], B], op2: Callable[[B], C]) -> Callable[[A], C]: # pylint: disable=function-redefined - ... # pylint: disable=pointless-statement +def pipe(__op1: Callable[[A], B], __op2: Callable[[B], C]) -> Callable[[A], C]: # pylint: disable=function-redefined + ... @overload -def pipe(op1: Callable[[A], B], # pylint: disable=function-redefined - op2: Callable[[B], C], - op3: Callable[[C], D] - ) -> Callable[[A], D]: - ... # pylint: disable=pointless-statement +def pipe( + __op1: Callable[[A], B], __op2: Callable[[B], C], __op3: Callable[[C], D] # pylint: disable=function-redefined +) -> Callable[[A], D]: + ... @overload -def pipe(op1: Callable[[A], B], # pylint: disable=function-redefined - op2: Callable[[B], C], - op3: Callable[[C], D], - op4: Callable[[D], E] - ) -> Callable[[A], E]: - ... # pylint: disable=pointless-statement +def pipe( + __op1: Callable[[A], B], + __op2: Callable[[B], C], + __op3: Callable[[C], D], + __op4: Callable[[D], E], +) -> Callable[[A], E]: + ... @overload -def pipe(op1: Callable[[A], B], # pylint: disable=function-redefined - op2: Callable[[B], C], - op3: Callable[[C], D], - op4: Callable[[D], E], - op5: Callable[[E], F] - ) -> Callable[[A], F]: - ... # pylint: disable=pointless-statement +def pipe( + __op1: Callable[[A], B], + __op2: Callable[[B], C], + __op3: Callable[[C], D], + __op4: Callable[[D], E], + __op5: Callable[[E], F], +) -> Callable[[A], F]: + ... @overload -def pipe(op1: Callable[[A], B], # pylint: disable=function-redefined,too-many-arguments - op2: Callable[[B], C], - op3: Callable[[C], D], - op4: Callable[[D], E], - op5: Callable[[E], F], - op6: Callable[[F], G] - ) -> Callable[[A], G]: - ... # pylint: disable=pointless-statement +def pipe( + __op1: Callable[[A], B], + __op2: Callable[[B], C], + __op3: Callable[[C], D], + __op4: Callable[[D], E], + __op5: Callable[[E], F], + __op6: Callable[[F], G], +) -> Callable[[A], G]: + ... -# pylint: disable=function-redefined def pipe(*operators: Callable[[Any], Any]) -> Callable[[Any], Any]: """Compose multiple operators left to right. @@ -104,4 +101,5 @@ def pipe(*operators: Callable[[Any], Any]) -> Callable[[Any], Any]: def compose(source: Any) -> Any: return reduce(lambda obs, op: op(obs), operators, source) + return compose diff --git a/rx/core/run.py b/rx/core/run.py index 4d784e8e6..0bd8249ec 100644 --- a/rx/core/run.py +++ b/rx/core/run.py @@ -3,8 +3,8 @@ from rx.internal.exceptions import SequenceContainsNoElementsError from rx.scheduler import NewThreadScheduler -from .observable import Observable +from .observable import Observable scheduler = NewThreadScheduler() diff --git a/rx/core/typing.py b/rx/core/typing.py index d9c13e3c0..2c742fa42 100644 --- a/rx/core/typing.py +++ b/rx/core/typing.py @@ -1,334 +1,63 @@ -from abc import abstractmethod -from typing import Any, Callable, Generic, Optional, Tuple, TypeVar, Union -from datetime import datetime, timedelta from threading import Thread +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union -from . import abc +from .abc.observable import Subscription +from .abc.observer import OnCompleted, OnError, OnNext +from .abc.periodicscheduler import (ScheduledPeriodicAction, + ScheduledSingleOrPeriodicAction) +from .abc.scheduler import (AbsoluteOrRelativeTime, AbsoluteTime, RelativeTime, + ScheduledAction) +from .abc.startable import StartableBase -T_out = TypeVar('T_out', covariant=True) -T_in = TypeVar('T_in', contravariant=True) -TState = TypeVar('TState') # Can be anything -T1 = TypeVar('T1') -T2 = TypeVar('T2') +if TYPE_CHECKING: + # Futures cannot take generic argument before Python 3.9 + class Future: + """Mock future for type testing.""" + result: Any = None + add_done_callback: Any = None + cancel: Any = None -Action = Callable[[], None] - -OnNext = Callable[[Any], None] -OnError = Callable[[Exception], None] -OnCompleted = Callable[[], None] - -Mapper = Callable[[T1], T2] -MapperIndexed = Callable[[T1, int], T2] -Predicate = Callable[[T1], bool] -PredicateIndexed = Callable[[T1, int], bool] -Comparer = Callable[[T1, T2], bool] -SubComparer = Callable[[T1, T2], int] -Accumulator = Callable[[TState, T1], TState] -AbsoluteTime = Union[datetime, float] -RelativeTime = Union[timedelta, float] -AbsoluteOrRelativeTime = Union[datetime, timedelta, float] - - -class Disposable(abc.Disposable): - """Disposable abstract base class.""" - - __slots__ = () - - @abstractmethod - def dispose(self) -> None: - """Dispose the object: stop whatever we're doing and release all of the - resources we might be using. - """ - - raise NotImplementedError - - -class Scheduler(abc.Scheduler): - """Scheduler abstract base class.""" - - __slots__ = () - - @property - @abstractmethod - def now(self) -> datetime: - """Represents a notion of time for this scheduler. Tasks being - scheduled on a scheduler will adhere to the time denoted by this - property. - - Returns: - The scheduler's current time, as a datetime instance. - """ - - return NotImplemented - - @abstractmethod - def schedule(self, - action: 'ScheduledAction', - state: Optional[TState] = None - ) -> Disposable: - """Schedules an action to be executed. - - Args: - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented - - @abstractmethod - def schedule_relative(self, - duetime: RelativeTime, - action: 'ScheduledAction', - state: Optional[TState] = None - ) -> Disposable: - """Schedules an action to be executed after duetime. - - Args: - duetime: Relative time after which to execute the action. - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented - - @abstractmethod - def schedule_absolute(self, - duetime: AbsoluteTime, - action: 'ScheduledAction', - state: Optional[TState] = None - ) -> Disposable: - """Schedules an action to be executed at duetime. - - Args: - duetime: Absolute time at which to execute the action. - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented - - @classmethod - @abstractmethod - def to_seconds(cls, value: AbsoluteOrRelativeTime) -> float: - """Converts time value to seconds. This method handles both absolute - (datetime) and relative (timedelta) values. If the argument is already - a float, it is simply returned unchanged. - - Args: - value: the time value to convert to seconds. - - Returns: - The value converted to seconds. - """ - - return NotImplemented - - @classmethod - @abstractmethod - def to_datetime(cls, value: AbsoluteOrRelativeTime) -> datetime: - """Converts time value to datetime. This method handles both absolute - (float) and relative (timedelta) values. If the argument is already - a datetime, it is simply returned unchanged. - - Args: - value: the time value to convert to datetime. - - Returns: - The value converted to datetime. - """ - - return NotImplemented - - @classmethod - @abstractmethod - def to_timedelta(cls, value: AbsoluteOrRelativeTime) -> timedelta: - """Converts time value to timedelta. This method handles both absolute - (datetime) and relative (float) values. If the argument is already - a timedelta, it is simply returned unchanged. If the argument is an - absolute time, the result value will be the timedelta since the epoch, - January 1st, 1970, 00:00:00. - - Args: - value: the time value to convert to timedelta. - - Returns: - The value converted to timedelta. - """ - - return NotImplemented - - -class PeriodicScheduler(abc.PeriodicScheduler): - """PeriodicScheduler abstract base class.""" - - __slots__ = () +else: + from asyncio.futures import Future - @abstractmethod - def schedule_periodic(self, - period: RelativeTime, - action: 'ScheduledPeriodicAction', - state: Optional[TState] = None - ) -> Disposable: - """Schedules a periodic piece of work. - - Args: - period: Period in seconds or timedelta for running the - work periodically. - action: Action to be executed. - state: [Optional] Initial state passed to the action upon - the first iteration. - - Returns: - The disposable object used to cancel the scheduled - recurring action (best effort). - """ - - return NotImplemented +_TState = TypeVar("_TState") # Can be anything +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +Action = Callable[[], None] -ScheduledAction = Callable[[Scheduler, Optional[TState]], Optional[Disposable]] -ScheduledPeriodicAction = Callable[[Optional[TState]], Optional[TState]] -ScheduledSingleOrPeriodicAction = Union[ScheduledAction, ScheduledPeriodicAction] +Mapper = Callable[[_T1], _T2] +MapperIndexed = Callable[[_T1, int], _T2] +Predicate = Callable[[_T1], bool] +PredicateIndexed = Callable[[_T1, int], bool] +Comparer = Callable[[_T1, _T2], bool] +SubComparer = Callable[[_T1, _T2], int] +Accumulator = Callable[[_TState, _T1], _TState] -Startable = Union[abc.Startable, Thread] +Startable = Union[StartableBase, Thread] StartableTarget = Callable[..., None] StartableFactory = Callable[[StartableTarget], Startable] - -class Observer(Generic[T_in], abc.Observer): - """Observer abstract base class - - An Observer is the entity that receives all emissions of a subscribed - Observable. - """ - - __slots__ = () - - @abstractmethod - def on_next(self, value: T_in) -> None: - """Notifies the observer of a new element in the sequence. - - Args: - value: The received element. - """ - - raise NotImplementedError - - @abstractmethod - def on_error(self, error: Exception) -> None: - """Notifies the observer that an exception has occurred. - - Args: - error: The error that has occurred. - """ - - raise NotImplementedError - - @abstractmethod - def on_completed(self) -> None: - """Notifies the observer of the end of the sequence.""" - - raise NotImplementedError - - -class Observable(Generic[T_out], abc.Observable): - """Observable abstract base class. - - Represents a push-style collection.""" - - __slots__ = () - - @abstractmethod - def subscribe(self, - observer: Observer[T_out] = None, - *, - scheduler: Scheduler = None - ) -> Disposable: - """Subscribe an observer to the observable sequence. - - Args: - observer: [Optional] The object that is to receive - notifications. - scheduler: [Optional] The default scheduler to use for this - subscription. - - Returns: - Disposable object representing an observer's subscription - to the observable sequence. - """ - - raise NotImplementedError - - -class Subject(Generic[T_in, T_out], abc.Subject): - """Subject abstract base class. - - Represents an object that is both an observable sequence as well - as an observer. - """ - - __slots__ = () - - @abstractmethod - def subscribe(self, - observer: Observer[T_out] = None, - *, - scheduler: Scheduler = None - ) -> Disposable: - """Subscribe an observer to the observable sequence. - - Args: - observer: [Optional] The object that is to receive - notifications. - scheduler: [Optional] The default scheduler to use for this - subscription. - - Returns: - Disposable object representing an observer's subscription - to the observable sequence. - """ - - raise NotImplementedError - - @abstractmethod - def on_next(self, value: T_in) -> None: - """Notifies the observer of a new element in the sequence. - - Args: - value: The received element. - """ - - raise NotImplementedError - - @abstractmethod - def on_error(self, error: Exception) -> None: - """Notifies the observer that an exception has occurred. - - Args: - error: The error that has occurred. - """ - - raise NotImplementedError - - @abstractmethod - def on_completed(self) -> None: - """Notifies the observer of the end of the sequence.""" - - raise NotImplementedError - - -Subscription = Callable[[Observer, Optional[Scheduler]], Disposable] +__all__ = [ + "Accumulator", + "AbsoluteTime", + "AbsoluteOrRelativeTime", + "Mapper", + "MapperIndexed", + "OnNext", + "OnError", + "OnCompleted", + "Predicate", + "PredicateIndexed", + "RelativeTime", + "SubComparer", + "ScheduledPeriodicAction", + "ScheduledSingleOrPeriodicAction", + "ScheduledAction", + "Startable", + "StartableTarget", + "Subscription", + "Future", +] diff --git a/rx/disposable/__init__.py b/rx/disposable/__init__.py index 70038b912..fb0b814d3 100644 --- a/rx/disposable/__init__.py +++ b/rx/disposable/__init__.py @@ -1,8 +1,8 @@ -from .disposable import Disposable from .booleandisposable import BooleanDisposable from .compositedisposable import CompositeDisposable -from .singleassignmentdisposable import SingleAssignmentDisposable +from .disposable import Disposable from .multipleassignmentdisposable import MultipleAssignmentDisposable -from .serialdisposable import SerialDisposable from .refcountdisposable import RefCountDisposable from .scheduleddisposable import ScheduledDisposable +from .serialdisposable import SerialDisposable +from .singleassignmentdisposable import SingleAssignmentDisposable diff --git a/rx/disposable/booleandisposable.py b/rx/disposable/booleandisposable.py index fe49fd827..02bf4a7f8 100644 --- a/rx/disposable/booleandisposable.py +++ b/rx/disposable/booleandisposable.py @@ -1,8 +1,9 @@ from threading import RLock -from rx.core.typing import Disposable +from rx.core.abc import DisposableBase -class BooleanDisposable(Disposable): + +class BooleanDisposable(DisposableBase): """Represents a Disposable that can be checked for status.""" def __init__(self): diff --git a/rx/disposable/compositedisposable.py b/rx/disposable/compositedisposable.py index ff1615347..22d21665e 100644 --- a/rx/disposable/compositedisposable.py +++ b/rx/disposable/compositedisposable.py @@ -1,15 +1,16 @@ from threading import RLock +from typing import Any, List -from rx.core.typing import Disposable +from rx.core.abc import DisposableBase -class CompositeDisposable(Disposable): +class CompositeDisposable(DisposableBase): """Represents a group of disposable resources that are disposed together""" - def __init__(self, *args): + def __init__(self, *args: Any): if args and isinstance(args[0], list): - self.disposable = args[0] + self.disposable: List[DisposableBase] = args[0] else: self.disposable = list(args) @@ -17,7 +18,7 @@ def __init__(self, *args): self.lock = RLock() super(CompositeDisposable, self).__init__() - def add(self, item): + def add(self, item: DisposableBase): """Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed @@ -34,7 +35,7 @@ def add(self, item): if should_dispose: item.dispose() - def remove(self, item): + def remove(self, item: DisposableBase): """Removes and disposes the first occurrence of a disposable from the CompositeDisposable.""" @@ -52,7 +53,7 @@ def remove(self, item): return should_dispose - def dispose(self): + def dispose(self) -> None: """Disposes all disposable in the group and removes them from the group.""" @@ -67,7 +68,7 @@ def dispose(self): for disp in current_disposable: disp.dispose() - def clear(self): + def clear(self) -> None: """Removes and disposes all disposable from the CompositeDisposable, but does not dispose the CompositeDisposable.""" @@ -79,7 +80,7 @@ def clear(self): for disposable in current_disposable: disposable.dispose() - def contains(self, item): + def contains(self, item: DisposableBase) -> bool: """Determines whether the CompositeDisposable contains a specific disposable. diff --git a/rx/disposable/disposable.py b/rx/disposable/disposable.py index 5e6c2af95..4deb7f6d4 100644 --- a/rx/disposable/disposable.py +++ b/rx/disposable/disposable.py @@ -1,11 +1,13 @@ -from typing import Optional from threading import RLock +from typing import Optional -from rx.internal import noop from rx.core import typing +from rx.core.abc import DisposableBase +from rx.core.typing import Action +from rx.internal import noop -class Disposable(typing.Disposable): +class Disposable(DisposableBase): """Main disposable class""" def __init__(self, action: Optional[typing.Action] = None) -> None: @@ -22,7 +24,7 @@ def __init__(self, action: Optional[typing.Action] = None) -> None: """ self.is_disposed = False - self.action = action or noop + self.action: Action = action or noop self.lock = RLock() diff --git a/rx/disposable/multipleassignmentdisposable.py b/rx/disposable/multipleassignmentdisposable.py index ea45d8a18..df6c0c83c 100644 --- a/rx/disposable/multipleassignmentdisposable.py +++ b/rx/disposable/multipleassignmentdisposable.py @@ -1,22 +1,24 @@ from threading import RLock -from rx.core.typing import Disposable +from typing import Optional +from rx.core.abc import DisposableBase -class MultipleAssignmentDisposable(Disposable): + +class MultipleAssignmentDisposable(DisposableBase): """Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource.""" def __init__(self): - self.current = None + self.current: Optional[DisposableBase] = None self.is_disposed = False self.lock = RLock() super().__init__() - def get_disposable(self): + def get_disposable(self) -> Optional[DisposableBase]: return self.current - def set_disposable(self, value): + def set_disposable(self, value: DisposableBase): """If the MultipleAssignmentDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object.""" diff --git a/rx/disposable/refcountdisposable.py b/rx/disposable/refcountdisposable.py index 1273c84d6..5ed8301c5 100644 --- a/rx/disposable/refcountdisposable.py +++ b/rx/disposable/refcountdisposable.py @@ -1,18 +1,18 @@ from threading import RLock +from typing import Optional +from rx.core.abc import DisposableBase from rx.disposable import Disposable -from rx.core import typing -class RefCountDisposable(typing.Disposable): +class RefCountDisposable(DisposableBase): """Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed.""" - class InnerDisposable(typing.Disposable): - - def __init__(self, parent) -> None: - self.parent = parent + class InnerDisposable(DisposableBase): + def __init__(self, parent: "RefCountDisposable") -> None: + self.parent: Optional[RefCountDisposable] = parent self.is_disposed = False self.lock = RLock() @@ -20,9 +20,11 @@ def dispose(self) -> None: with self.lock: parent = self.parent self.parent = None - parent.release() - def __init__(self, disposable) -> None: + if parent is not None: + parent.release() + + def __init__(self, disposable: DisposableBase) -> None: """Initializes a new instance of the RefCountDisposable class with the specified disposable.""" @@ -67,7 +69,7 @@ def release(self) -> None: self.underlying_disposable.dispose() @property - def disposable(self) -> typing.Disposable: + def disposable(self) -> DisposableBase: """Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable.""" diff --git a/rx/disposable/scheduleddisposable.py b/rx/disposable/scheduleddisposable.py index bc0d603f5..04acb36e6 100644 --- a/rx/disposable/scheduleddisposable.py +++ b/rx/disposable/scheduleddisposable.py @@ -1,12 +1,14 @@ from threading import RLock -from rx.core.typing import Disposable +from typing import Any +from rx.core.abc import DisposableBase, SchedulerBase -class ScheduledDisposable(Disposable): + +class ScheduledDisposable(DisposableBase): """Represents a disposable resource whose disposal invocation will be scheduled on the specified Scheduler""" - def __init__(self, scheduler, disposable) -> None: + def __init__(self, scheduler: SchedulerBase, disposable: DisposableBase) -> None: """Initializes a new instance of the ScheduledDisposable class that uses a Scheduler on which to dispose the disposable.""" @@ -22,7 +24,7 @@ def dispose(self) -> None: parent = self - def action(scheduler, state): + def action(scheduler: SchedulerBase, state: Any): """Scheduled dispose action""" should_dispose = False diff --git a/rx/disposable/serialdisposable.py b/rx/disposable/serialdisposable.py index d1c8fe283..cfea05be0 100644 --- a/rx/disposable/serialdisposable.py +++ b/rx/disposable/serialdisposable.py @@ -1,33 +1,33 @@ from threading import RLock from typing import Optional -from rx.core import typing -from rx.core.typing import Disposable +from rx.core import abc +from rx.core.abc import DisposableBase -class SerialDisposable(Disposable): +class SerialDisposable(DisposableBase): """Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource. """ def __init__(self) -> None: - self.current: Optional[Disposable] = None + self.current: Optional[DisposableBase] = None self.is_disposed = False self.lock = RLock() super().__init__() - def get_disposable(self) -> Optional[Disposable]: + def get_disposable(self) -> Optional[DisposableBase]: return self.current - def set_disposable(self, value: typing.Disposable) -> None: + def set_disposable(self, value: abc.DisposableBase) -> None: """If the SerialDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object. Assigning this property disposes the previous disposable object.""" - old: Optional[Disposable] = None + old: Optional[DisposableBase] = None with self.lock: should_dispose = self.is_disposed @@ -47,7 +47,7 @@ def dispose(self) -> None: """Disposes the underlying disposable as well as all future replacements.""" - old: Optional[Disposable] = None + old: Optional[DisposableBase] = None with self.lock: if not self.is_disposed: diff --git a/rx/disposable/singleassignmentdisposable.py b/rx/disposable/singleassignmentdisposable.py index 9784d3e70..d767b9f42 100644 --- a/rx/disposable/singleassignmentdisposable.py +++ b/rx/disposable/singleassignmentdisposable.py @@ -1,8 +1,10 @@ from threading import RLock -from rx.core.typing import Disposable +from typing import Optional +from rx.core.abc import DisposableBase -class SingleAssignmentDisposable(Disposable): + +class SingleAssignmentDisposable(DisposableBase): """Single assignment disposable. Represents a disposable resource which only allows a single @@ -15,19 +17,19 @@ def __init__(self) -> None: class. """ self.is_disposed = False - self.current = None + self.current: Optional[DisposableBase] = None self.lock = RLock() super().__init__() - def get_disposable(self): + def get_disposable(self) -> Optional[DisposableBase]: return self.current - def set_disposable(self, value): + def set_disposable(self, value: DisposableBase): if self.current: - raise Exception('Disposable has already been assigned') + raise Exception("Disposable has already been assigned") - old = None + old: Optional[DisposableBase] = None with self.lock: should_dispose = self.is_disposed @@ -35,7 +37,7 @@ def set_disposable(self, value): old = self.current self.current = value - if old: + if old is not None: old.dispose() if should_dispose and value: @@ -45,7 +47,7 @@ def set_disposable(self, value): def dispose(self) -> None: """Sets the status to disposed""" - old = None + old: Optional[DisposableBase] = None with self.lock: if not self.is_disposed: diff --git a/rx/internal/__init__.py b/rx/internal/__init__.py index ccd28a427..ffbc729fd 100644 --- a/rx/internal/__init__.py +++ b/rx/internal/__init__.py @@ -1,5 +1,5 @@ +from . import concurrency, constants +from .basic import default_comparer, default_error, noop +from .exceptions import (ArgumentOutOfRangeException, DisposedException, + SequenceContainsNoElementsError) from .priorityqueue import PriorityQueue -from .basic import noop, default_error, default_comparer -from .exceptions import SequenceContainsNoElementsError, ArgumentOutOfRangeException, DisposedException -from . import concurrency -from . import constants diff --git a/rx/internal/basic.py b/rx/internal/basic.py index 77e0a8256..18791d8cb 100644 --- a/rx/internal/basic.py +++ b/rx/internal/basic.py @@ -1,9 +1,9 @@ -from typing import Any from datetime import datetime +from typing import Any, NoReturn, Union # Defaults -def noop(*args, **kw): +def noop(*args: Any, **kw: Any): """No operation. Returns nothing""" pass @@ -21,7 +21,7 @@ def default_comparer(x: Any, y: Any) -> bool: return x == y -def default_sub_comparer(x, y): +def default_sub_comparer(x: Any, y: Any) -> Any: return x - y @@ -29,8 +29,8 @@ def default_key_serializer(x: Any) -> str: return str(x) -def default_error(err) -> Exception: +def default_error(err: Union[Exception, str]) -> NoReturn: if isinstance(err, BaseException): raise err - else: - raise Exception(err) + + raise Exception(err) diff --git a/rx/internal/exceptions.py b/rx/internal/exceptions.py index ae5afa6f2..bdc1a62da 100644 --- a/rx/internal/exceptions.py +++ b/rx/internal/exceptions.py @@ -1,31 +1,34 @@ # Rx Exceptions +from typing import Optional + + class SequenceContainsNoElementsError(Exception): - def __init__(self, msg=None): + def __init__(self, msg: Optional[str] = None): super(SequenceContainsNoElementsError, self).__init__(msg or "Sequence contains no elements") class ArgumentOutOfRangeException(ValueError): - def __init__(self, msg=None): + def __init__(self, msg: Optional[str] = None): super(ArgumentOutOfRangeException, self).__init__(msg or "Argument out of range") class DisposedException(Exception): - def __init__(self, msg=None): + def __init__(self, msg: Optional[str] = None): super(DisposedException, self).__init__(msg or "Object has been disposed") class ReEntracyException(Exception): - def __init__(self, msg=None): - super(ReEntracyException, self).__init__(msg or 'Re-entrancy detected') + def __init__(self, msg: Optional[str] = None): + super(ReEntracyException, self).__init__(msg or "Re-entrancy detected") class CompletedException(Exception): - def __init__(self, msg=None): - super(CompletedException, self).__init__(msg or 'Observer completed') + def __init__(self, msg: Optional[str] = None): + super(CompletedException, self).__init__(msg or "Observer completed") class WouldBlockException(Exception): - def __init__(self, msg=None): + def __init__(self, msg: Optional[str] = None): super(WouldBlockException, self).__init__(msg or "Would block") diff --git a/rx/internal/priorityqueue.py b/rx/internal/priorityqueue.py index 466f24212..1d91f7c87 100644 --- a/rx/internal/priorityqueue.py +++ b/rx/internal/priorityqueue.py @@ -1,17 +1,17 @@ import heapq from sys import maxsize -from typing import Generic, List, Tuple +from typing import Generic, List, Tuple, TypeVar -from rx.core.typing import T1 +_T1 = TypeVar("_T1") -class PriorityQueue(Generic[T1]): +class PriorityQueue(Generic[_T1]): """Priority queue for scheduling. Note that methods aren't thread-safe.""" MIN_COUNT = ~maxsize def __init__(self) -> None: - self.items: List[Tuple[T1, int]] = [] + self.items: List[Tuple[_T1, int]] = [] self.count = PriorityQueue.MIN_COUNT # Monotonic increasing for sort stability def __len__(self): @@ -19,25 +19,25 @@ def __len__(self): return len(self.items) - def peek(self) -> T1: + def peek(self) -> _T1: """Returns first item in queue without removing it""" return self.items[0][0] - def dequeue(self) -> T1: + def dequeue(self) -> _T1: """Returns and removes item with lowest priority from queue""" - item: T1 = heapq.heappop(self.items)[0] + item: _T1 = heapq.heappop(self.items)[0] if not self.items: self.count = PriorityQueue.MIN_COUNT return item - def enqueue(self, item: T1) -> None: + def enqueue(self, item: _T1) -> None: """Adds item to queue""" heapq.heappush(self.items, (item, self.count)) self.count += 1 - def remove(self, item: T1) -> bool: + def remove(self, item: _T1) -> bool: """Remove given item from queue""" for index, _item in enumerate(self.items): diff --git a/rx/internal/utils.py b/rx/internal/utils.py index cadae5e28..39e5df21b 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -1,21 +1,22 @@ from functools import update_wrapper from types import FunctionType -from typing import cast, Any, Callable, Iterable +from typing import Any, Callable, Iterable, Optional, cast +from rx.core.abc import ObservableBase, ObserverBase, SchedulerBase from rx.disposable import CompositeDisposable -def add_ref(xs, r): +def add_ref(xs: ObservableBase[Any], r): from rx.core import Observable - def subscribe(observer, scheduler=None): + def subscribe(observer: ObserverBase[Any], scheduler: Optional[SchedulerBase] = None): return CompositeDisposable(r.disposable, xs.subscribe(observer)) return Observable(subscribe) def is_future(fut: Any) -> bool: - return callable(getattr(fut, 'add_done_callback', None)) + return callable(getattr(fut, "add_done_callback", None)) def infinite() -> Iterable[int]: @@ -30,13 +31,9 @@ def alias(name: str, doc: str, fun: Callable[..., Any]) -> Callable[..., Any]: # See also help(type(lambda: 0)) _fun = cast(FunctionType, fun) args = (_fun.__code__, _fun.__globals__) - kwargs = { - 'name': name, - 'argdefs': _fun.__defaults__, - 'closure': _fun.__closure__ - } + kwargs = {"name": name, "argdefs": _fun.__defaults__, "closure": _fun.__closure__} alias = FunctionType(*args, **kwargs) # type: ignore - alias = cast(FunctionType, update_wrapper(alias, _fun)) + alias = update_wrapper(alias, _fun) alias.__kwdefaults__ = _fun.__kwdefaults__ alias.__doc__ = doc return alias @@ -45,8 +42,8 @@ def alias(name: str, doc: str, fun: Callable[..., Any]) -> Callable[..., Any]: class NotSet: """Sentinel value.""" - def __eq__(self, other): + def __eq__(self, other: Any): return self is other def __repr__(self): - return 'NotSet' + return "NotSet" diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 2e52383e3..306bd576f 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1,16 +1,20 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin from asyncio import Future -from typing import Callable, Union, Any, Iterable, List, Optional, cast, overload -from datetime import timedelta, datetime +from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar, Union, cast +from rx.core import ConnectableObservable, GroupedObservable, Observable, abc, pipe, typing +from rx.core.typing import Accumulator, Comparer, Mapper, MapperIndexed, Predicate, PredicateIndexed from rx.internal.utils import NotSet -from rx.core import Observable, ConnectableObservable, GroupedObservable, typing, pipe -from rx.core.typing import Mapper, MapperIndexed, Predicate, PredicateIndexed, Comparer, Accumulator from rx.subject import Subject +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_TState = TypeVar("_TState") -def all(predicate: Predicate) -> Callable[[Observable], Observable]: + +def all(predicate: Predicate) -> Callable[[Observable[_T]], Observable[_T]]: """Determines whether all elements of an observable sequence satisfy a condition. @@ -34,10 +38,11 @@ def all(predicate: Predicate) -> Callable[[Observable], Observable]: the test in the specified predicate. """ from rx.core.operators.all import _all + return _all(predicate) -def amb(right_source: Observable) -> Callable[[Observable], Observable]: +def amb(right_source: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Propagates the observable sequence that reacts first. .. marble:: @@ -58,10 +63,11 @@ def amb(right_source: Observable) -> Callable[[Observable], Observable]: sequences, whichever reacted first. """ from rx.core.operators.amb import _amb + return _amb(right_source) -def as_observable() -> Callable[[Observable], Observable]: +def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: """Hides the identity of an observable sequence. Returns: @@ -70,6 +76,7 @@ def as_observable() -> Callable[[Observable], Observable]: source sequence. """ from rx.core.operators.asobservable import _as_observable + return _as_observable() @@ -100,6 +107,7 @@ def average(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Obser the average of the sequence of values. """ from rx.core.operators.average import _average + return _average(key_mapper) @@ -127,6 +135,7 @@ def buffer(boundaries: Observable) -> Callable[[Observable], Observable]: observable sequence of buffers. """ from rx.core.operators.buffer import _buffer + return _buffer(boundaries) @@ -158,12 +167,13 @@ def buffer_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observabl observable sequence of windows. """ from rx.core.operators.buffer import _buffer_when + return _buffer_when(closing_mapper) -def buffer_toggle(openings: Observable, - closing_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: +def buffer_toggle( + openings: Observable, closing_mapper: Callable[[Any], Observable] +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more buffers. @@ -193,6 +203,7 @@ def buffer_toggle(openings: Observable, observable sequence of windows. """ from rx.core.operators.buffer import _buffer_toggle + return _buffer_toggle(openings, closing_mapper) @@ -222,13 +233,15 @@ def buffer_with_count(count: int, skip: Optional[int] = None) -> Callable[[Obser observable sequence of buffers. """ from rx.core.operators.buffer import _buffer_with_count + return _buffer_with_count(count, skip) -def buffer_with_time(timespan: typing.RelativeTime, - timeshift: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def buffer_with_time( + timespan: typing.RelativeTime, + timeshift: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more buffers which are produced based on timing information. @@ -260,6 +273,7 @@ def buffer_with_time(timespan: typing.RelativeTime, returns an observable sequence of buffers. """ from rx.core.operators.bufferwithtime import _buffer_with_time + return _buffer_with_time(timespan, timeshift, scheduler) @@ -292,11 +306,13 @@ def buffer_with_time_or_count(timespan, count, scheduler=None) -> Callable[[Obse returns an observable sequence of buffers. """ from rx.core.operators.bufferwithtimeorcount import _buffer_with_time_or_count + return _buffer_with_time_or_count(timespan, count, scheduler) -def catch(handler: Union[Observable, Callable[[Exception, Observable], Observable]] - ) -> Callable[[Observable], Observable]: +def catch( + handler: Union[Observable, Callable[[Exception, Observable], Observable]] +) -> Callable[[Observable], Observable]: """Continues an observable sequence that is terminated by an exception with the next observable sequence. @@ -326,6 +342,7 @@ def catch(handler: Union[Observable, Callable[[Exception, Observable], Observabl exception occurred. """ from rx.core.operators.catch import _catch + return _catch(handler) @@ -352,6 +369,7 @@ def combine_latest(*others: Observable) -> Callable[[Observable], Observable]: combining elements of the sources into a tuple. """ from rx.core.operators.combinelatest import _combine_latest + return _combine_latest(*others) @@ -375,12 +393,11 @@ def concat(*sources: Observable) -> Callable[[Observable], Observable]: each given sequence, in sequential order. """ from rx.core.operators.concat import _concat + return _concat(*sources) -def contains(value: Any, - comparer: Optional[typing.Comparer] = None - ) -> Callable[[Observable], Observable]: +def contains(value: Any, comparer: Optional[typing.Comparer] = None) -> Callable[[Observable], Observable]: """Determines whether an observable sequence contains a specified element with an optional equality comparer. @@ -406,6 +423,7 @@ def contains(value: Any, specified value. """ from rx.core.operators.contains import _contains + return _contains(value, comparer) @@ -437,12 +455,13 @@ def count(predicate: Optional[typing.Predicate] = None) -> Callable[[Observable] """ from rx.core.operators.count import _count + return _count(predicate) -def debounce(duetime: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def debounce( + duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Ignores values from an observable sequence which are followed by another value before duetime. @@ -466,6 +485,7 @@ def debounce(duetime: typing.RelativeTime, returns the debounced observable sequence. """ from rx.core.operators.debounce import _debounce + return _debounce(duetime, scheduler) @@ -498,12 +518,13 @@ def default_if_empty(default_value: Any = None) -> Callable[[Observable], Observ the source. """ from rx.core.operators.defaultifempty import _default_if_empty + return _default_if_empty(default_value) -def delay_subscription(duetime: typing.AbsoluteOrRelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def delay_subscription( + duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Time shifts the observable sequence by delaying the subscription. @@ -527,12 +548,11 @@ def delay_subscription(duetime: typing.AbsoluteOrRelativeTime, time-shifted observable sequence. """ from rx.core.operators.delaysubscription import _delay_subscription + return _delay_subscription(duetime, scheduler=scheduler) -def delay_with_mapper(subscription_delay=None, - delay_duration_mapper=None - ) -> Callable[[Observable], Observable]: +def delay_with_mapper(subscription_delay=None, delay_duration_mapper=None) -> Callable[[Observable], Observable]: """Time shifts the observable sequence based on a subscription delay and a delay mapper function for each element. @@ -560,6 +580,7 @@ def delay_with_mapper(subscription_delay=None, time-shifted observable sequence. """ from rx.core.operators.delaywithmapper import _delay_with_mapper + return _delay_with_mapper(subscription_delay, delay_duration_mapper) @@ -574,12 +595,13 @@ def dematerialize() -> Callable[[Observable], Observable]: corresponding to the source sequence's notification values. """ from rx.core.operators.dematerialize import _dematerialize + return _dematerialize() -def delay(duetime: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def delay( + duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """The delay operator. .. marble:: @@ -607,12 +629,13 @@ def delay(duetime: typing.RelativeTime, observable and returns a time-shifted sequence. """ from rx.core.operators.delay import _delay + return _delay(duetime, scheduler) -def distinct(key_mapper: Optional[Mapper] = None, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def distinct( + key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: """Returns an observable sequence that contains only distinct elements according to the key_mapper and the comparer. Usage of this operator should be considered carefully due to the maintenance @@ -643,12 +666,13 @@ def distinct(key_mapper: Optional[Mapper] = None, sequence. """ from rx.core.operators.distinct import _distinct + return _distinct(key_mapper, comparer) -def distinct_until_changed(key_mapper: Optional[Mapper] = None, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def distinct_until_changed( + key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: """Returns an observable sequence that contains only distinct contiguous elements according to the key_mapper and the comparer. @@ -678,10 +702,11 @@ def distinct_until_changed(key_mapper: Optional[Mapper] = None, source sequence. """ from rx.core.operators.distinctuntilchanged import _distinct_until_changed + return _distinct_until_changed(key_mapper, comparer) -def do(observer: typing.Observer) -> Callable[[Observable], Observable]: +def do(observer: abc.ObserverBase[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional termination of the observable sequence. This method can be used for debugging, @@ -707,13 +732,15 @@ def do(observer: typing.Observer) -> Callable[[Observable], Observable]: applied. """ from rx.core.operators.do import do as do_ + return do_(observer) -def do_action(on_next: Optional[typing.OnNext] = None, - on_error: Optional[typing.OnError] = None, - on_completed: Optional[typing.OnCompleted] = None - ) -> Callable[[Observable], Observable]: +def do_action( + on_next: Optional[typing.OnNext] = None, + on_error: Optional[typing.OnError] = None, + on_completed: Optional[typing.OnCompleted] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional termination of the observable sequence. This method can be used for debugging, @@ -746,10 +773,11 @@ def do_action(on_next: Optional[typing.OnNext] = None, applied. """ from rx.core.operators.do import _do_action + return _do_action(on_next, on_error, on_completed) -def do_while(condition: Predicate) -> Callable[[Observable], Observable]: +def do_while(condition: Predicate) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a do while loop. @@ -770,10 +798,11 @@ def do_while(condition: Predicate) -> Callable[[Observable], Observable]: as the condition holds. """ from rx.core.operators.dowhile import _do_while + return _do_while(condition) -def element_at(index: int) -> Callable[[Observable], Observable]: +def element_at(index: int) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the element at a specified index in a sequence. .. marble:: @@ -795,10 +824,11 @@ def element_at(index: int) -> Callable[[Observable], Observable]: the specified position in the source sequence. """ from rx.core.operators.elementatordefault import _element_at_or_default + return _element_at_or_default(index, False) -def element_at_or_default(index: int, default_value: Any = None) -> Callable[[Observable], Observable]: +def element_at_or_default(index: int, default_value: Any = None) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the element at a specified index in a sequence or a default value if the index is out of range. @@ -825,10 +855,11 @@ def element_at_or_default(index: int, default_value: Any = None) -> Callable[[Ob the index is outside the bounds of the source sequence. """ from rx.core.operators.elementatordefault import _element_at_or_default + return _element_at_or_default(index, True, default_value) -def exclusive() -> Callable[[Observable], Observable]: +def exclusive() -> Callable[[Observable[_T]], Observable[_T]]: """Performs a exclusive waiting for the first to finish before subscribing to another observable. Observables that come in between subscriptions will be dropped on the floor. @@ -849,10 +880,11 @@ def exclusive() -> Callable[[Observable], Observable]: happen when subscribed. """ from rx.core.operators.exclusive import _exclusive + return _exclusive() -def expand(mapper: Mapper) -> Callable[[Observable], Observable]: +def expand(mapper: Mapper) -> Callable[[Observable[_T]], Observable[_T]]: """Expands an observable sequence by recursively invoking mapper. Args: @@ -865,10 +897,11 @@ def expand(mapper: Mapper) -> Callable[[Observable], Observable]: by the recursive expansion. """ from rx.core.operators.expand import _expand + return _expand(mapper) -def filter(predicate: Predicate) -> Callable[[Observable], Observable]: +def filter(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Filters the elements of an observable sequence based on a predicate. @@ -892,10 +925,13 @@ def filter(predicate: Predicate) -> Callable[[Observable], Observable]: input sequence that satisfy the condition. """ from rx.core.operators.filter import _filter + return _filter(predicate) -def filter_indexed(predicate_indexed: Optional[PredicateIndexed] = None) -> Callable[[Observable], Observable]: +def filter_indexed( + predicate_indexed: Optional[PredicateIndexed[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Filters the elements of an observable sequence based on a predicate by incorporating the element's index. @@ -920,6 +956,7 @@ def filter_indexed(predicate_indexed: Optional[PredicateIndexed] = None) -> Call input sequence that satisfy the condition. """ from rx.core.operators.filter import _filter_indexed + return _filter_indexed(predicate_indexed) @@ -948,6 +985,7 @@ def finally_action(action: Callable) -> Callable[[Observable], Observable]: termination behavior applied. """ from rx.core.operators.finallyaction import _finally_action + return _finally_action(action) @@ -974,6 +1012,7 @@ def find(predicate: Predicate) -> Callable[[Observable], Observable]: found otherwise, None. """ from rx.core.operators.find import _find_value + return _find_value(predicate, False) @@ -1001,6 +1040,7 @@ def find_index(predicate: Predicate) -> Callable[[Observable], Observable]: defined by match, if found; otherwise, -1. """ from rx.core.operators.find import _find_value + return _find_value(predicate, True) @@ -1032,12 +1072,13 @@ def first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obser predicate if provided, else the first item in the sequence. """ from rx.core.operators.first import _first + return _first(predicate) -def first_or_default(predicate: Optional[Predicate] = None, - default_value: Any = None - ) -> Callable[[Observable], Observable]: +def first_or_default( + predicate: Optional[Predicate[_T]] = None, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the first element of an observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. @@ -1068,10 +1109,11 @@ def first_or_default(predicate: Optional[Predicate] = None, predicate, or a default value if no such element exists. """ from rx.core.operators.firstordefault import _first_or_default + return _first_or_default(predicate, default_value) -def flat_map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def flat_map(mapper: Optional[Mapper[_T1, Observable[_T2]]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: """The flat_map operator. .. marble:: @@ -1110,10 +1152,13 @@ def flat_map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observab the input sequence. """ from rx.core.operators.flatmap import _flat_map + return _flat_map(mapper) -def flat_map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable[[Observable], Observable]: +def flat_map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None, +) -> Callable[[Observable[_T1]], Observable[_T2]]: """The `flat_map_indexed` operator. One of the Following: @@ -1151,6 +1196,7 @@ def flat_map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable the input sequence. """ from rx.core.operators.flatmap import _flat_map_indexed + return _flat_map_indexed(mapper_indexed) @@ -1175,6 +1221,7 @@ def flat_map_latest(mapper: Mapper) -> Callable[[Observable], Observable]: observable sequence that has been received. """ from rx.core.operators.flatmap import _flat_map_latest + return _flat_map_latest(mapper) @@ -1202,13 +1249,15 @@ def fork_join(*others: Observable) -> Callable[[Observable], Observable]: of combining last element from each source in given sequence. """ from rx.core.operators.forkjoin import _fork_join + return _fork_join(*others) -def group_by(key_mapper: Mapper, - element_mapper: Optional[Mapper] = None, - subject_mapper: Optional[Callable[[], Subject]] = None, - ) -> Callable[[Observable], Observable]: +def group_by( + key_mapper: Mapper, + element_mapper: Optional[Mapper] = None, + subject_mapper: Optional[Callable[[], Subject]] = None, +) -> Callable[[Observable], Observable]: """Groups the elements of an observable sequence according to a specified key mapper function and comparer and selects the resulting elements by using a specified function. @@ -1241,14 +1290,16 @@ def group_by(key_mapper: Mapper, share that same key value. """ from rx.core.operators.groupby import _group_by + return _group_by(key_mapper, element_mapper, subject_mapper) -def group_by_until(key_mapper: Mapper, - element_mapper: Optional[Mapper], - duration_mapper: Callable[[GroupedObservable], Observable], - subject_mapper: Optional[Callable[[], Subject]] = None, - ) -> Callable[[Observable], Observable]: +def group_by_until( + key_mapper: Mapper, + element_mapper: Optional[Mapper], + duration_mapper: Callable[[GroupedObservable], Observable], + subject_mapper: Optional[Callable[[], Subject]] = None, +) -> Callable[[Observable], Observable]: """Groups the elements of an observable sequence according to a specified key mapper function. A duration mapper function is used to control the lifetime of groups. When a group expires, it @@ -1287,13 +1338,15 @@ def group_by_until(key_mapper: Mapper, with such a key value is encountered. """ from rx.core.operators.groupbyuntil import _group_by_until + return _group_by_until(key_mapper, element_mapper, duration_mapper, subject_mapper) -def group_join(right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: +def group_join( + right: Observable, + left_duration_mapper: Callable[[Any], Observable], + right_duration_mapper: Callable[[Any], Observable], +) -> Callable[[Observable], Observable]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. @@ -1322,6 +1375,7 @@ def group_join(right: Observable, duration. """ from rx.core.operators.groupjoin import _group_join + return _group_join(right, left_duration_mapper, right_duration_mapper) @@ -1342,10 +1396,11 @@ def ignore_elements() -> Callable[[Observable], Observable]: successful or exceptional, of the source sequence. """ from rx.core.operators.ignoreelements import _ignore_elements + return _ignore_elements() -def is_empty() -> Callable[[Observable], Observable]: +def is_empty() -> Callable[[Observable[Any]], Observable[bool]]: """Determines whether an observable sequence is empty. .. marble:: @@ -1362,13 +1417,15 @@ def is_empty() -> Callable[[Observable], Observable]: determining whether the source sequence is empty. """ from rx.core.operators.isempty import _is_empty + return _is_empty() -def join(right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: +def join( + right: Observable, + left_duration_mapper: Callable[[Any], Observable], + right_duration_mapper: Callable[[Any], Observable], +) -> Callable[[Observable], Observable]: """Correlates the elements of two sequences based on overlapping durations. @@ -1396,10 +1453,11 @@ def join(right: Observable, duration. """ from rx.core.operators.join import _join + return _join(right, left_duration_mapper, right_duration_mapper) -def last(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: +def last(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: """The last operator. Returns the last element of an observable sequence that satisfies @@ -1427,12 +1485,13 @@ def last(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observ predicate. """ from rx.core.operators.last import _last + return _last(predicate) -def last_or_default(predicate: Optional[Predicate] = None, - default_value: Any = None - ) -> Callable[[Observable], Observable]: +def last_or_default( + predicate: Optional[Predicate[_T]] = None, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """The last_or_default operator. Returns the last element of an observable sequence that satisfies @@ -1466,10 +1525,11 @@ def last_or_default(predicate: Optional[Predicate] = None, predicate, or a default value if no such element exists. """ from rx.core.operators.lastordefault import _last_or_default - return _last_or_default(predicate, default_value) + + return _last_or_default(predicate, default_value) -def map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def map(mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: """The map operator. Project each element of an observable sequence into a new form. @@ -1495,10 +1555,13 @@ def map(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: of the source. """ from rx.core.operators.map import _map + return _map(mapper) -def map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable[[Observable], Observable]: +def map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: """Project each element of an observable sequence into a new form by incorporating the element's index. @@ -1524,6 +1587,7 @@ def map_indexed(mapper_indexed: Optional[MapperIndexed] = None) -> Callable[[Obs of the source. """ from rx.core.operators.map import _map_indexed + return _map_indexed(mapper_indexed) @@ -1537,6 +1601,7 @@ def materialize() -> Callable[[Observable], Observable]: notification values from the source sequence. """ from rx.core.operators.materialize import _materialize + return _materialize() @@ -1564,12 +1629,11 @@ def max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl element with the maximum element in the source sequence. """ from rx.core.operators.max import _max + return _max(comparer) -def max_by(key_mapper: Mapper, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def max_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: """The max_by operator. Returns the elements in an observable sequence with the maximum @@ -1596,12 +1660,11 @@ def max_by(key_mapper: Mapper, zero or more elements that have a maximum key value. """ from rx.core.operators.maxby import _max_by + return _max_by(key_mapper, comparer) -def merge(*sources: Observable, - max_concurrent: Optional[int] = None - ) -> Callable[[Observable], Observable]: +def merge(*sources: Observable, max_concurrent: Optional[int] = None) -> Callable[[Observable], Observable]: """Merges an observable sequence of observable sequences into an observable sequence, limiting the number of concurrent subscriptions to inner sequences. Or merges two observable @@ -1630,6 +1693,7 @@ def merge(*sources: Observable, inner sequences. """ from rx.core.operators.merge import _merge + return _merge(*sources, max_concurrent=max_concurrent) @@ -1653,6 +1717,7 @@ def merge_all() -> Callable[[Observable], Observable]: elements of the inner sequences. """ from rx.core.operators.merge import _merge_all + return _merge_all() @@ -1682,12 +1747,11 @@ def min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl with the minimum element in the source sequence. """ from rx.core.operators.min import _min + return _min(comparer) -def min_by(key_mapper: Mapper, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def min_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: """The `min_by` operator. Returns the elements in an observable sequence with the minimum key @@ -1714,13 +1778,15 @@ def min_by(key_mapper: Mapper, or more elements that have a minimum key value. """ from rx.core.operators.minby import _min_by + return _min_by(key_mapper, comparer) -def multicast(subject: Optional[typing.Subject] = None, - subject_factory: Optional[Callable[[Optional[typing.Scheduler]], typing.Subject]] = None, - mapper: Optional[Callable[[ConnectableObservable], Observable]] = None - ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: +def multicast( + subject: Optional[abc.SubjectBase] = None, + subject_factory: Optional[Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase]] = None, + mapper: Optional[Callable[[ConnectableObservable], Observable]] = None, +) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: """Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a mapper function. Each subscription to the resulting sequence causes a @@ -1749,10 +1815,11 @@ def multicast(subject: Optional[typing.Subject] = None, mapper function. """ from rx.core.operators.multicast import _multicast + return _multicast(subject, subject_factory, mapper) -def observe_on(scheduler: typing.Scheduler) -> Callable[[Observable], Observable]: +def observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: """Wraps the source sequence in order to run its observer callbacks on the specified scheduler. @@ -1769,6 +1836,7 @@ def observe_on(scheduler: typing.Scheduler) -> Callable[[Observable], Observable specified scheduler. """ from rx.core.operators.observeon import _observe_on + return _observe_on(scheduler) @@ -1796,6 +1864,7 @@ def on_error_resume_next(second: Observable) -> Callable[[Observable], Observabl """ from rx.core.operators.onerrorresumenext import _on_error_resume_next + return _on_error_resume_next(second) @@ -1814,6 +1883,7 @@ def pairwise() -> Callable[[Observable], Observable]: observations from the input observable as an array. """ from rx.core.operators.pairwise import _pairwise + return _pairwise() @@ -1845,6 +1915,7 @@ def partition(predicate: Predicate) -> Callable[[Observable], List[Observable]]: predicate returns False. """ from rx.core.operators.partition import _partition + return _partition(predicate) @@ -1877,6 +1948,7 @@ def partition_indexed(predicate_indexed: PredicateIndexed) -> Callable[[Observab returns False. """ from rx.core.operators.partition import _partition_indexed + return _partition_indexed(predicate_indexed) @@ -1894,6 +1966,7 @@ def pluck(key: Any) -> Callable[[Observable], Observable]: returns a new observable sequence of key values. """ from rx.core.operators.pluck import _pluck + return _pluck(key) @@ -1912,6 +1985,7 @@ def pluck_attr(prop: str) -> Callable[[Observable], Observable]: returns a new observable sequence of property values. """ from rx.core.operators.pluck import _pluck_attr + return _pluck_attr(prop) @@ -1942,6 +2016,7 @@ def publish(mapper: Optional[Mapper] = None) -> Callable[[Observable], Connectab mapper function. """ from rx.core.operators.publish import _publish + return _publish(mapper) @@ -1976,10 +2051,13 @@ def publish_value(initial_value: Any, mapper: Optional[Mapper] = None) -> Callab mapper function. """ from rx.core.operators.publishvalue import _publish_value + return _publish_value(initial_value, mapper) -def reduce(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable], Observable]: +def reduce( + accumulator: Accumulator[_TState, _T], seed: Union[_T, NotSet] = NotSet +) -> Callable[[Observable[_T]], Observable[_TState]]: """The reduce operator. Applies an accumulator function over an observable sequence, @@ -2012,6 +2090,7 @@ def reduce(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable element with the final accumulator value. """ from rx.core.operators.reduce import _reduce + return _reduce(accumulator, seed) @@ -2021,10 +2100,11 @@ def ref_count() -> Callable[[ConnectableObservable], Observable]: observable sequence. """ from rx.core.operators.connectable.refcount import _ref_count + return _ref_count() -def repeat(repeat_count: Optional[int] = None) -> Callable[[Observable], Observable]: +def repeat(repeat_count: Optional[int] = None) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the observable sequence a specified number of times. If the repeat count is not specified, the sequence repeats indefinitely. @@ -2050,14 +2130,16 @@ def repeat(repeat_count: Optional[int] = None) -> Callable[[Observable], Observa given sequence repeatedly. """ from rx.core.operators.repeat import _repeat + return _repeat(repeat_count) -def replay(mapper: Optional[Mapper] = None, - buffer_size: Optional[int] = None, - window: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: +def replay( + mapper: Optional[Mapper] = None, + buffer_size: Optional[int] = None, + window: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: """The `replay` operator. Returns an observable sequence that is the result of invoking the @@ -2093,10 +2175,11 @@ def replay(mapper: Optional[Mapper] = None, mapper function. """ from rx.core.operators.replay import _replay + return _replay(mapper, buffer_size, window, scheduler=scheduler) -def retry(retry_count: Optional[int] = None) -> Callable[[Observable], Observable]: +def retry(retry_count: Optional[int] = None) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the source observable sequence the specified number of times or until it successfully terminates. If the retry count is not specified, it retries indefinitely. @@ -2114,12 +2197,13 @@ def retry(retry_count: Optional[int] = None) -> Callable[[Observable], Observabl sequence repeatedly until it terminates successfully. """ from rx.core.operators.retry import _retry + return _retry(retry_count) -def sample(sampler: Union[typing.RelativeTime, Observable], - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def sample( + sampler: Union[typing.RelativeTime, Observable[Any]], scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Samples the observable sequence at each interval. .. marble:: @@ -2144,10 +2228,13 @@ def sample(sampler: Union[typing.RelativeTime, Observable], returns a sampled observable sequence. """ from rx.core.operators.sample import _sample + return _sample(sampler, scheduler) -def scan(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable], Observable]: +def scan( + accumulator: Accumulator[_TState, _T], seed: Union[_T, NotSet] = NotSet +) -> Callable[[Observable[_T]], Observable[_TState]]: """The scan operator. Applies an accumulator function over an observable sequence and @@ -2177,11 +2264,11 @@ def scan(accumulator: Accumulator, seed: Any = NotSet) -> Callable[[Observable], accumulated values. """ from rx.core.operators.scan import _scan + return _scan(accumulator, seed) -def sequence_equal(second: Observable, comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def sequence_equal(second: Observable, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: """Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer. @@ -2212,10 +2299,11 @@ def sequence_equal(second: Observable, comparer: Optional[Comparer] = None specified equality comparer. """ from rx.core.operators.sequenceequal import _sequence_equal + return _sequence_equal(second, comparer) -def share() -> Callable[[Observable], Observable]: +def share() -> Callable[[Observable[_T]], Observable[_T]]: """Share a single subscription among multiple observers. This is an alias for a composed publish() and ref_count(). @@ -2230,10 +2318,11 @@ def share() -> Callable[[Observable], Observable]: Observable. """ from rx.core.operators.publish import _share + return _share() -def single(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: +def single(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: """The single operator. Returns the only element of an observable sequence that satisfies @@ -2262,12 +2351,13 @@ def single(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obse predicate. """ from rx.core.operators.single import _single + return _single(predicate) -def single_or_default(predicate: Optional[Predicate] = None, - default_value: Any = None - ) -> Callable[[Observable], Observable]: +def single_or_default( + predicate: Optional[Predicate[_T]] = None, default_value: Any = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that matches the predicate, or a default value if no such element exists this method reports an exception if there is more than one element in @@ -2299,17 +2389,19 @@ def single_or_default(predicate: Optional[Predicate] = None, predicate, or a default value if no such element exists. """ from rx.core.operators.singleordefault import _single_or_default + return _single_or_default(predicate, default_value) -def single_or_default_async(has_default: bool = False, - default_value: Any = None - ) -> Callable[[Observable], Observable]: +def single_or_default_async( + has_default: bool = False, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: from rx.core.operators.singleordefault import _single_or_default_async + return _single_or_default_async(has_default, default_value) -def skip(count: int) -> Callable[[Observable], Observable]: +def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: """The skip operator. Bypasses a specified number of elements in an observable sequence @@ -2333,10 +2425,11 @@ def skip(count: int) -> Callable[[Observable], Observable]: occur after the specified index in the input sequence. """ from rx.core.operators.skip import _skip + return _skip(count) -def skip_last(count: int) -> Callable[[Observable], Observable]: +def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: """The skip_last operator. .. marble:: @@ -2365,11 +2458,13 @@ def skip_last(count: int) -> Callable[[Observable], Observable]: elements except for the bypassed ones at the end. """ from rx.core.operators.skiplast import _skip_last + return _skip_last(count) -def skip_last_with_time(duration: typing.RelativeTime, scheduler: typing.Scheduler = None - ) -> Callable[[Observable], Observable]: +def skip_last_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements for the specified duration from the end of the observable source sequence. @@ -2392,10 +2487,11 @@ def skip_last_with_time(duration: typing.RelativeTime, scheduler: typing.Schedul specified duration from the end of the source sequence. """ from rx.core.operators.skiplastwithtime import _skip_last_with_time + return _skip_last_with_time(duration, scheduler=scheduler) -def skip_until(other: Observable) -> Callable[[Observable], Observable]: +def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after the other observable sequence produces a value. @@ -2418,12 +2514,13 @@ def skip_until(other: Observable) -> Callable[[Observable], Observable]: triggered propagation. """ from rx.core.operators.skipuntil import _skip_until + return _skip_until(other) -def skip_until_with_time(start_time: typing.AbsoluteOrRelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def skip_until_with_time( + start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements from the observable source sequence until the specified start time. Errors produced by the source sequence are always forwarded to the @@ -2451,10 +2548,11 @@ def skip_until_with_time(start_time: typing.AbsoluteOrRelativeTime, until the specified start time. """ from rx.core.operators.skipuntilwithtime import _skip_until_with_time + return _skip_until_with_time(start_time, scheduler=scheduler) -def skip_while(predicate: typing.Predicate) -> Callable[[Observable], Observable]: +def skip_while(predicate: typing.Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """The `skip_while` operator. Bypasses elements in an observable sequence as long as a specified @@ -2483,10 +2581,11 @@ def skip_while(predicate: typing.Predicate) -> Callable[[Observable], Observable series that does not pass the test specified by predicate. """ from rx.core.operators.skipwhile import _skip_while + return _skip_while(predicate) -def skip_while_indexed(predicate: typing.PredicateIndexed) -> Callable[[Observable], Observable]: +def skip_while_indexed(predicate: typing.PredicateIndexed[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. The element's index is used in the logic of the predicate @@ -2514,11 +2613,13 @@ def skip_while_indexed(predicate: typing.PredicateIndexed) -> Callable[[Observab series that does not pass the test specified by predicate. """ from rx.core.operators.skipwhile import _skip_while_indexed + return _skip_while_indexed(predicate) -def skip_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def skip_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements for the specified duration from the start of the observable source sequence. @@ -2551,13 +2652,13 @@ def skip_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Sch the specified duration from the start of the source sequence. """ from rx.core.operators.skipwithtime import _skip_with_time + return _skip_with_time(duration, scheduler=scheduler) -def slice(start: Optional[int] = None, - stop: Optional[int] = None, - step: Optional[int] = None - ) -> Callable[[Observable], Observable]: +def slice( + start: Optional[int] = None, stop: Optional[int] = None, step: Optional[int] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """The slice operator. Slices the given observable. It is basically a wrapper around the operators @@ -2589,10 +2690,11 @@ def slice(start: Optional[int] = None, returns a sliced observable sequence. """ from rx.core.operators.slice import _slice + return _slice(start, stop, step) -def some(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: +def some(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[bool]]: """The some operator. Determines whether some element of an observable sequence @@ -2621,10 +2723,11 @@ def some(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observ items are in the sequence. """ from rx.core.operators.some import _some + return _some(predicate) -def starmap(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def starmap(mapper: Optional[Callable[..., _T]] = None) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: """The starmap operator. Unpack arguments grouped as tuple elements of an observable @@ -2662,8 +2765,9 @@ def starmap(mapper: Optional[Mapper] = None) -> Callable[[Observable], Observabl return pipe(map(lambda values: cast(Mapper, mapper)(*values))) -def starmap_indexed(mapper: Optional[MapperIndexed] = None - ) -> Callable[[Observable], Observable]: +def starmap_indexed( + mapper: Optional[Callable[..., _T]] = None +) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: """Variant of :func:`starmap` which accepts an indexed mapper. .. marble:: @@ -2711,10 +2815,11 @@ def start_with(*args: Any) -> Callable[[Observable], Observable]: the source sequence prepended with the specified values. """ from rx.core.operators.startswith import _start_with + return _start_with(*args) -def subscribe_on(scheduler: typing.Scheduler) -> Callable[[Observable], Observable]: +def subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: """Subscribe on the specified scheduler. Wrap the source sequence in order to run its subscription and @@ -2736,10 +2841,11 @@ def subscribe_on(scheduler: typing.Scheduler) -> Callable[[Observable], Observab un-subscriptions happen on the specified scheduler. """ from rx.core.operators.subscribeon import _subscribe_on + return _subscribe_on(scheduler) -def sum(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def sum(key_mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: """Computes the sum of a sequence of values that are obtained by invoking an optional transform function on each element of the input sequence, else if not specified computes the sum on each item @@ -2766,10 +2872,11 @@ def sum(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observabl of the values in the source sequence. """ from rx.core.operators.sum import _sum + return _sum(key_mapper) -def switch_latest() -> Callable[[Observable], Observable]: +def switch_latest() -> Callable[[Observable[Observable[Any]]], Observable[Any]]: """The switch_latest operator. Transforms an observable sequence of observable sequences into an @@ -2792,10 +2899,11 @@ def switch_latest() -> Callable[[Observable], Observable]: sequence that has been received. """ from rx.core.operators.switchlatest import _switch_latest + return _switch_latest() -def take(count: int) -> Callable[[Observable], Observable]: +def take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: """Returns a specified number of contiguous elements from the start of an observable sequence. @@ -2818,10 +2926,11 @@ def take(count: int) -> Callable[[Observable], Observable]: number of elements from the start of the input sequence. """ from rx.core.operators.take import _take + return _take(count) -def take_last(count: int) -> Callable[[Observable], Observable]: +def take_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: """Returns a specified number of contiguous elements from the end of an observable sequence. @@ -2850,10 +2959,11 @@ def take_last(count: int) -> Callable[[Observable], Observable]: of elements from the end of the source sequence. """ from rx.core.operators.takelast import _take_last + return _take_last(count) -def take_last_buffer(count: int) -> Callable[[Observable], Observable]: +def take_last_buffer(count: int) -> Callable[[Observable[_T]], Observable[_T]]: """The `take_last_buffer` operator. Returns an array with the specified number of contiguous elements @@ -2885,12 +2995,13 @@ def take_last_buffer(count: int) -> Callable[[Observable], Observable]: sequence. """ from rx.core.operators.takelastbuffer import _take_last_buffer + return _take_last_buffer(count) -def take_last_with_time(duration: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def take_last_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns elements within the specified duration from the end of the observable source sequence. @@ -2921,10 +3032,11 @@ def take_last_with_time(duration: typing.RelativeTime, sequence. """ from rx.core.operators.takelastwithtime import _take_last_with_time + return _take_last_with_time(duration, scheduler=scheduler) -def take_until(other: Observable) -> Callable[[Observable], Observable]: +def take_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence until the other observable sequence produces a value. @@ -2947,12 +3059,13 @@ def take_until(other: Observable) -> Callable[[Observable], Observable]: further propagation. """ from rx.core.operators.takeuntil import _take_until + return _take_until(other) -def take_until_with_time(end_time: typing.AbsoluteOrRelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def take_until_with_time( + end_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Takes elements for the specified duration until the specified end time, using the specified scheduler to run timers. @@ -2980,6 +3093,7 @@ def take_until_with_time(end_time: typing.AbsoluteOrRelativeTime, the specified end time. """ from rx.core.operators.takeuntilwithtime import _take_until_with_time + return _take_until_with_time(end_time, scheduler=scheduler) @@ -3011,6 +3125,7 @@ def take_while(predicate: Predicate, inclusive: bool = False) -> Callable[[Obser test no longer passes. """ from rx.core.operators.takewhile import _take_while + return _take_while(predicate, inclusive) @@ -3043,11 +3158,13 @@ def take_while_indexed(predicate: PredicateIndexed, inclusive: bool = False) -> longer passes. """ from rx.core.operators.takewhile import _take_while_indexed + return _take_while_indexed(predicate, inclusive) -def take_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def take_with_time( + duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Takes elements for the specified duration from the start of the observable source sequence. @@ -3077,12 +3194,13 @@ def take_with_time(duration: typing.RelativeTime, scheduler: Optional[typing.Sch the specified duration from the start of the source sequence. """ from rx.core.operators.takewithtime import _take_with_time + return _take_with_time(duration, scheduler=scheduler) -def throttle_first(window_duration: typing.RelativeTime, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def throttle_first( + window_duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Returns an Observable that emits only the first item emitted by the source Observable during sequential time windows of a specified duration. @@ -3096,6 +3214,7 @@ def throttle_first(window_duration: typing.RelativeTime, returns an observable that performs the throttle operation. """ from rx.core.operators.throttlefirst import _throttle_first + return _throttle_first(window_duration, scheduler) @@ -3118,10 +3237,11 @@ def throttle_with_mapper(throttle_duration_mapper: Callable[[Any], Observable]) source and returns the throttled observable sequence. """ from rx.core.operators.debounce import _throttle_with_mapper + return _throttle_with_mapper(throttle_duration_mapper) -def timestamp(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def timestamp(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: """The timestamp operator. Records the timestamp for each value in an observable sequence. @@ -3138,13 +3258,13 @@ def timestamp(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observa information on values. """ from rx.core.operators.timestamp import _timestamp + return _timestamp(scheduler=scheduler) -def timeout(duetime: typing.AbsoluteTime, - other: Optional[Observable] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def timeout( + duetime: typing.AbsoluteTime, other: Optional[Observable] = None, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Returns the source observable sequence or the other observable sequence if duetime elapses. @@ -3175,13 +3295,15 @@ def timeout(duetime: typing.AbsoluteTime, case of a timeout. """ from rx.core.operators.timeout import _timeout + return _timeout(duetime, other, scheduler) -def timeout_with_mapper(first_timeout: Optional[Observable] = None, - timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, - other: Optional[Observable] = None - ) -> Callable[[Observable], Observable]: +def timeout_with_mapper( + first_timeout: Optional[Observable] = None, + timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, + other: Optional[Observable] = None, +) -> Callable[[Observable], Observable]: """Returns the source observable sequence, switching to the other observable sequence if a timeout is signaled. @@ -3206,10 +3328,11 @@ def timeout_with_mapper(first_timeout: Optional[Observable] = None, case of a timeout. """ from rx.core.operators.timeoutwithmapper import _timeout_with_mapper + return _timeout_with_mapper(first_timeout, timeout_duration_mapper, other) -def time_interval(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Observable], Observable]: +def time_interval(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: """Records the time interval between consecutive values in an observable sequence. @@ -3229,11 +3352,11 @@ def time_interval(scheduler: Optional[typing.Scheduler] = None) -> Callable[[Obs on values. """ from rx.core.operators.timeinterval import _time_interval + return _time_interval(scheduler=scheduler) -def to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None - ) -> Callable[[Observable], Observable]: +def to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: """Converts the observable sequence to a Map if it exists. Args: @@ -3249,6 +3372,7 @@ def to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None dictionary containing the values from the observable sequence. """ from rx.core.operators.todict import _to_dict + return _to_dict(key_mapper, element_mapper) @@ -3266,6 +3390,7 @@ def to_future(future_ctor: Optional[Callable[[], Future]] = None) -> Callable[[O a future with the last value from the observable sequence. """ from rx.core.operators.tofuture import _to_future + return _to_future(future_ctor) @@ -3280,15 +3405,16 @@ def to_iterable() -> Callable[[Observable], Observable]: an iterable containing all the elements of the source sequence. """ from rx.core.operators.toiterable import _to_iterable + return _to_iterable() to_list = to_iterable -def to_marbles(timespan: typing.RelativeTime = 0.1, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def to_marbles( + timespan: typing.RelativeTime = 0.1, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: """Convert an observable sequence into a marble diagram string. Args: @@ -3301,6 +3427,7 @@ def to_marbles(timespan: typing.RelativeTime = 0.1, Observable stream. """ from rx.core.operators.tomarbles import _to_marbles + return _to_marbles(scheduler=scheduler, timespan=timespan) @@ -3313,6 +3440,7 @@ def to_set() -> Callable[[Observable], Observable]: containing the values from the observable sequence. """ from rx.core.operators.toset import _to_set + return _to_set() @@ -3330,6 +3458,7 @@ def while_do(condition: Predicate) -> Callable[[Observable], Observable]: condition holds. """ from rx.core.operators.whiledo import _while_do + return _while_do(condition) @@ -3362,6 +3491,7 @@ def window(boundaries: Observable) -> Callable[[Observable], Observable]: """ from rx.core.operators.window import _window + return _window(boundaries) @@ -3397,12 +3527,13 @@ def window_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observabl returns an observable sequence of windows. """ from rx.core.operators.window import _window_when + return _window_when(closing_mapper) -def window_toggle(openings: Observable, - closing_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: +def window_toggle( + openings: Observable, closing_mapper: Callable[[Any], Observable] +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more windows. @@ -3432,6 +3563,7 @@ def window_toggle(openings: Observable, returns an observable sequence of windows. """ from rx.core.operators.window import _window_toggle + return _window_toggle(openings, closing_mapper) @@ -3462,21 +3594,25 @@ def window_with_count(count: int, skip: Optional[int] = None) -> Callable[[Obser An observable sequence of windows. """ from rx.core.operators.windowwithcount import _window_with_count + return _window_with_count(count, skip) -def window_with_time(timespan: typing.RelativeTime, - timeshift: Optional[typing.RelativeTime] = None, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def window_with_time( + timespan: typing.RelativeTime, + timeshift: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: from rx.core.operators.windowwithtime import _window_with_time + return _window_with_time(timespan, timeshift, scheduler) -def window_with_time_or_count(timespan: typing.RelativeTime, count: int, - scheduler: Optional[typing.Scheduler] = None - ) -> Callable[[Observable], Observable]: +def window_with_time_or_count( + timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None +) -> Callable[[Observable], Observable]: from rx.core.operators.windowwithtimeorcount import _window_with_time_or_count + return _window_with_time_or_count(timespan, count, scheduler) @@ -3506,6 +3642,7 @@ def with_latest_from(*sources: Observable) -> Callable[[Observable], Observable] combining elements of the sources into a tuple. """ from rx.core.operators.withlatestfrom import _with_latest_from + return _with_latest_from(*sources) @@ -3536,10 +3673,11 @@ def zip(*args: Observable) -> Callable[[Observable], Observable]: combining elements of the sources as a tuple. """ from rx.core.operators.zip import _zip + return _zip(*args) -def zip_with_iterable(second: Iterable) -> Callable[[Observable], Observable]: +def zip_with_iterable(second: Iterable[_T2]) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: """Merges the specified observable sequence and list into one observable sequence by creating a tuple whenever all of the observable sequences have produced an element at a @@ -3564,6 +3702,7 @@ def zip_with_iterable(second: Iterable) -> Callable[[Observable], Observable]: combining elements of the sources as a tuple. """ from rx.core.operators.zip import _zip_with_iterable + return _zip_with_iterable(second) diff --git a/rx/scheduler/__init__.py b/rx/scheduler/__init__.py index 61b1670ce..044b5a5ac 100644 --- a/rx/scheduler/__init__.py +++ b/rx/scheduler/__init__.py @@ -1,11 +1,10 @@ -from .scheduleditem import ScheduledItem - from .catchscheduler import CatchScheduler from .currentthreadscheduler import CurrentThreadScheduler from .eventloopscheduler import EventLoopScheduler from .historicalscheduler import HistoricalScheduler from .immediatescheduler import ImmediateScheduler from .newthreadscheduler import NewThreadScheduler +from .scheduleditem import ScheduledItem from .threadpoolscheduler import ThreadPoolScheduler from .timeoutscheduler import TimeoutScheduler from .trampolinescheduler import TrampolineScheduler diff --git a/rx/scheduler/catchscheduler.py b/rx/scheduler/catchscheduler.py index f0a4e1493..5eeccb1f3 100644 --- a/rx/scheduler/catchscheduler.py +++ b/rx/scheduler/catchscheduler.py @@ -1,18 +1,17 @@ from datetime import datetime -from typing import cast, Callable, Optional +from typing import Callable, Optional, TypeVar, cast -from rx.core import typing +from rx.core import abc, typing +from rx.core.abc.scheduler import ScheduledAction, SchedulerBase from rx.disposable import Disposable, SingleAssignmentDisposable from .periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") -class CatchScheduler(PeriodicScheduler): - def __init__(self, - scheduler: typing.Scheduler, - handler: Callable[[Exception], bool] - ) -> None: +class CatchScheduler(PeriodicScheduler): + def __init__(self, scheduler: abc.SchedulerBase, handler: Callable[[Exception], bool]) -> None: """Wraps a scheduler, passed as constructor argument, adding exception handling for scheduled actions. The handler should return True to indicate it handled the exception successfully. Falsy return values will @@ -25,10 +24,10 @@ def __init__(self, """ super().__init__() - self._scheduler: typing.Scheduler = scheduler + self._scheduler: abc.SchedulerBase = scheduler self._handler: Callable[[Exception], bool] = handler - self._recursive_original: Optional[typing.Scheduler] = None - self._recursive_wrapper: Optional['CatchScheduler'] = None + self._recursive_original: Optional[abc.SchedulerBase] = None + self._recursive_wrapper: Optional["CatchScheduler"] = None @property def now(self) -> datetime: @@ -42,10 +41,7 @@ def now(self) -> datetime: return self._scheduler.now - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -60,11 +56,9 @@ def schedule(self, action = self._wrap(action) return self._scheduler.schedule(action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -80,11 +74,9 @@ def schedule_relative(self, action = self._wrap(action) return self._scheduler.schedule_relative(duetime, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -100,11 +92,12 @@ def schedule_absolute(self, action = self._wrap(action) return self._scheduler.schedule_absolute(duetime, action, state=state) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work. Args: @@ -119,14 +112,14 @@ def schedule_periodic(self, recurring action (best effort). """ - schedule_periodic = getattr(self._scheduler, 'schedule_periodic') + schedule_periodic = getattr(self._scheduler, "schedule_periodic") if not callable(schedule_periodic): raise NotImplementedError disp: SingleAssignmentDisposable = SingleAssignmentDisposable() failed: bool = False - def periodic(state: Optional[typing.TState] = None) -> Optional[typing.TState]: + def periodic(state: Optional[_TState] = None) -> Optional[_TState]: nonlocal failed if failed: return None @@ -143,15 +136,13 @@ def periodic(state: Optional[typing.TState] = None) -> Optional[typing.TState]: disp.disposable = scheduler.schedule_periodic(period, periodic, state=state) return disp - def _clone(self, scheduler: typing.Scheduler) -> 'CatchScheduler': + def _clone(self, scheduler: abc.SchedulerBase) -> "CatchScheduler": return CatchScheduler(scheduler, self._handler) - def _wrap(self, action: typing.ScheduledAction) -> typing.ScheduledAction: + def _wrap(self, action: typing.ScheduledAction[_TState]) -> typing.ScheduledAction[_TState]: parent = self - def wrapped_action(self, - state: Optional[typing.TState] - ) -> Optional[typing.Disposable]: + def wrapped_action(self: CatchScheduler, state: Optional[_TState]) -> Optional[abc.DisposableBase]: try: return action(parent._get_recursive_wrapper(self), state) except Exception as ex: @@ -161,7 +152,7 @@ def wrapped_action(self, return wrapped_action - def _get_recursive_wrapper(self, scheduler) -> 'CatchScheduler': + def _get_recursive_wrapper(self, scheduler: SchedulerBase) -> "CatchScheduler": if self._recursive_wrapper is None or self._recursive_original != scheduler: self._recursive_original = scheduler wrapper = self._clone(scheduler) diff --git a/rx/scheduler/currentthreadscheduler.py b/rx/scheduler/currentthreadscheduler.py index a796bd1d5..ee7defd0e 100644 --- a/rx/scheduler/currentthreadscheduler.py +++ b/rx/scheduler/currentthreadscheduler.py @@ -1,6 +1,5 @@ import logging -from threading import current_thread, local, Thread - +from threading import Thread, current_thread, local from typing import MutableMapping from weakref import WeakKeyDictionary diff --git a/rx/scheduler/eventloop/asyncioscheduler.py b/rx/scheduler/eventloop/asyncioscheduler.py index 9d556a85c..01803838c 100644 --- a/rx/scheduler/eventloop/asyncioscheduler.py +++ b/rx/scheduler/eventloop/asyncioscheduler.py @@ -1,15 +1,15 @@ -import logging import asyncio - +import logging from datetime import datetime -from typing import Optional +from typing import Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler - +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -28,10 +28,7 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: super().__init__() self._loop: asyncio.AbstractEventLoop = loop - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -54,11 +51,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -86,11 +84,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/eventloop/asynciothreadsafescheduler.py b/rx/scheduler/eventloop/asynciothreadsafescheduler.py index 79880d41a..e3cb92fc5 100644 --- a/rx/scheduler/eventloop/asynciothreadsafescheduler.py +++ b/rx/scheduler/eventloop/asynciothreadsafescheduler.py @@ -1,14 +1,15 @@ -import logging import asyncio - +import logging from concurrent.futures import Future -from typing import List, Optional +from typing import TYPE_CHECKING, List, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from .asyncioscheduler import AsyncIOScheduler +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -28,10 +29,7 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: super().__init__(loop) - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -54,7 +52,10 @@ def dispose() -> None: handle.cancel() return - future: Future = Future() + if TYPE_CHECKING: + future: Future[int] = Future() + else: + future: Future = Future() def cancel_handle() -> None: handle.cancel() @@ -65,11 +66,9 @@ def cancel_handle() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -111,7 +110,10 @@ def do_cancel_handles(): do_cancel_handles() return - future: Future = Future() + if TYPE_CHECKING: + future: Future[int] = Future() + else: + future: Future = Future() def cancel_handle() -> None: do_cancel_handles() @@ -122,11 +124,9 @@ def cancel_handle() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -144,9 +144,9 @@ def schedule_absolute(self, def _on_self_loop_or_not_running(self): """ - Returns True if either self._loop is not running, or we're currently - executing on self._loop. In both cases, waiting for a future to be - resolved on the loop would result in a deadlock. + Returns True if either self._loop is not running, or we're currently + executing on self._loop. In both cases, waiting for a future to be + resolved on the loop would result in a deadlock. """ if not self._loop.is_running(): return True diff --git a/rx/scheduler/eventloop/eventletscheduler.py b/rx/scheduler/eventloop/eventletscheduler.py index 0fcb3a201..d2a58739e 100644 --- a/rx/scheduler/eventloop/eventletscheduler.py +++ b/rx/scheduler/eventloop/eventletscheduler.py @@ -1,14 +1,14 @@ import logging - from datetime import datetime -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler - +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -29,10 +29,7 @@ def __init__(self, eventlet: Any) -> None: super().__init__() self._eventlet = eventlet - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -56,11 +53,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -89,11 +87,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/eventloop/geventscheduler.py b/rx/scheduler/eventloop/geventscheduler.py index 073453e93..face23f5f 100644 --- a/rx/scheduler/eventloop/geventscheduler.py +++ b/rx/scheduler/eventloop/geventscheduler.py @@ -1,14 +1,14 @@ import logging - from datetime import datetime -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler - +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -29,10 +29,7 @@ def __init__(self, gevent: Any) -> None: super().__init__() self._gevent = gevent - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -56,11 +53,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -90,10 +88,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/eventloop/ioloopscheduler.py b/rx/scheduler/eventloop/ioloopscheduler.py index 0db09f3c7..8fe230e4b 100644 --- a/rx/scheduler/eventloop/ioloopscheduler.py +++ b/rx/scheduler/eventloop/ioloopscheduler.py @@ -1,13 +1,14 @@ import logging from datetime import datetime -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler - +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -29,10 +30,7 @@ def __init__(self, loop: Any) -> None: super().__init__() self._loop = loop - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -59,11 +57,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -90,16 +89,16 @@ def interval() -> None: def dispose() -> None: self._loop.remove_timeout(timer) - self.\ - _loop.remove_timeout(timer) + self._loop.remove_timeout(timer) return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/eventloop/twistedscheduler.py b/rx/scheduler/eventloop/twistedscheduler.py index bab813ba9..2e65ec578 100644 --- a/rx/scheduler/eventloop/twistedscheduler.py +++ b/rx/scheduler/eventloop/twistedscheduler.py @@ -1,14 +1,14 @@ import logging - from datetime import datetime -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler - +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -26,10 +26,7 @@ def __init__(self, reactor: Any) -> None: super().__init__() self._reactor = reactor - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -43,11 +40,12 @@ def schedule(self, return self.schedule_relative(0.0, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -76,11 +74,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/eventloopscheduler.py b/rx/scheduler/eventloopscheduler.py index 11e5466fb..9cd386839 100644 --- a/rx/scheduler/eventloopscheduler.py +++ b/rx/scheduler/eventloopscheduler.py @@ -1,30 +1,27 @@ import logging import threading from collections import deque -from datetime import timedelta -from typing import Deque, Optional +from typing import Deque, Optional, TypeVar -from rx.core import typing +from rx.core import abc, typing from rx.disposable import Disposable from rx.internal.concurrency import default_thread_factory from rx.internal.constants import DELTA_ZERO from rx.internal.exceptions import DisposedException from rx.internal.priorityqueue import PriorityQueue -from .scheduleditem import ScheduledItem from .periodicscheduler import PeriodicScheduler +from .scheduleditem import ScheduledItem +log = logging.getLogger("Rx") -log = logging.getLogger('Rx') +_TState = TypeVar("_TState") -class EventLoopScheduler(PeriodicScheduler, typing.Disposable): +class EventLoopScheduler(PeriodicScheduler, abc.DisposableBase): """Creates an object that schedules units of work on a designated thread.""" - def __init__(self, - thread_factory: Optional[typing.StartableFactory] = None, - exit_if_empty: bool = False - ) -> None: + def __init__(self, thread_factory: Optional[typing.StartableFactory] = None, exit_if_empty: bool = False) -> None: super().__init__() self._is_disposed = False @@ -36,10 +33,7 @@ def __init__(self, self._exit_if_empty = exit_if_empty - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -53,11 +47,9 @@ def schedule(self, return self.schedule_absolute(self.now, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -73,11 +65,9 @@ def schedule_relative(self, duetime = max(DELTA_ZERO, self.to_timedelta(duetime)) return self.schedule_absolute(self.now + duetime, action, state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -106,11 +96,12 @@ def schedule_absolute(self, return Disposable(si.cancel) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work. Args: diff --git a/rx/scheduler/historicalscheduler.py b/rx/scheduler/historicalscheduler.py index 235dddb03..594c2a004 100644 --- a/rx/scheduler/historicalscheduler.py +++ b/rx/scheduler/historicalscheduler.py @@ -1,6 +1,7 @@ from datetime import datetime from rx.core import typing + from .scheduler import UTC_ZERO from .virtualtimescheduler import VirtualTimeScheduler diff --git a/rx/scheduler/immediatescheduler.py b/rx/scheduler/immediatescheduler.py index f2a17454a..11f62582b 100644 --- a/rx/scheduler/immediatescheduler.py +++ b/rx/scheduler/immediatescheduler.py @@ -1,13 +1,15 @@ from threading import Lock -from typing import Optional, MutableMapping +from typing import MutableMapping, Optional, TypeVar from weakref import WeakKeyDictionary -from rx.core import typing +from rx.core import abc, typing from rx.internal.constants import DELTA_ZERO from rx.internal.exceptions import WouldBlockException from .scheduler import Scheduler +_TState = TypeVar("_TState") + class ImmediateScheduler(Scheduler): """Represents an object that schedules units of work to run immediately, @@ -17,10 +19,10 @@ class ImmediateScheduler(Scheduler): """ _lock = Lock() - _global: MutableMapping[type, 'ImmediateScheduler'] = WeakKeyDictionary() + _global: MutableMapping[type, "ImmediateScheduler"] = WeakKeyDictionary() @classmethod - def singleton(cls) -> 'ImmediateScheduler': + def singleton(cls) -> "ImmediateScheduler": with ImmediateScheduler._lock: try: self = ImmediateScheduler._global[cls] @@ -29,13 +31,10 @@ def singleton(cls) -> 'ImmediateScheduler': ImmediateScheduler._global[cls] = self return self - def __new__(cls) -> 'ImmediateScheduler': + def __new__(cls) -> "ImmediateScheduler": return cls.singleton() - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -49,11 +48,9 @@ def schedule(self, return self.invoke_action(action, state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -72,11 +69,9 @@ def schedule_relative(self, return self.invoke_action(action, state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/mainloop/__init__.py b/rx/scheduler/mainloop/__init__.py index 086c31fb4..768c0bbd1 100644 --- a/rx/scheduler/mainloop/__init__.py +++ b/rx/scheduler/mainloop/__init__.py @@ -1,5 +1,5 @@ from .gtkscheduler import GtkScheduler -from .tkinterscheduler import TkinterScheduler from .pygamescheduler import PyGameScheduler from .qtscheduler import QtScheduler +from .tkinterscheduler import TkinterScheduler from .wxscheduler import WxScheduler diff --git a/rx/scheduler/mainloop/gtkscheduler.py b/rx/scheduler/mainloop/gtkscheduler.py index dca20e57a..d5ec4b016 100644 --- a/rx/scheduler/mainloop/gtkscheduler.py +++ b/rx/scheduler/mainloop/gtkscheduler.py @@ -1,13 +1,16 @@ -from typing import cast, Any, Optional +from typing import Any, Optional, TypeVar, cast -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") + class GtkScheduler(PeriodicScheduler): - """ A scheduler that schedules work via the GLib main loop + """A scheduler that schedules work via the GLib main loop used in GTK+ applications. See https://wiki.gnome.org/Projects/PyGObject @@ -24,12 +27,13 @@ def __init__(self, glib: Any) -> None: super().__init__() self._glib = glib - def _gtk_schedule(self, - time: typing.AbsoluteOrRelativeTime, - action: typing.ScheduledSingleOrPeriodicAction, - state: Optional[typing.TState] = None, - periodic: bool = False - ) -> typing.Disposable: + def _gtk_schedule( + self, + time: typing.AbsoluteOrRelativeTime, + action: typing.ScheduledSingleOrPeriodicAction, + state: Optional[_TState] = None, + periodic: bool = False, + ) -> abc.DisposableBase: msecs = max(0, int(self.to_seconds(time) * 1000.0)) @@ -45,7 +49,7 @@ def timer_handler(_) -> bool: if periodic: state = cast(typing.ScheduledPeriodicAction, action)(state) else: - sad.disposable = self.invoke_action(cast(typing.ScheduledAction, action), state=state) + sad.disposable = self.invoke_action(cast(typing.ScheduledAction[_TState], action), state=state) return periodic @@ -57,10 +61,7 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -73,11 +74,12 @@ def schedule(self, """ return self._gtk_schedule(0.0, action, state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -91,11 +93,12 @@ def schedule_relative(self, """ return self._gtk_schedule(duetime, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -111,21 +114,22 @@ def schedule_absolute(self, duetime = self.to_datetime(duetime) return self._gtk_schedule(duetime - self.now, action, state=state) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction, + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. - Args: - period: Period in seconds for running the work repeatedly. - action: Action to be executed. - state: [Optional] state to be given to the action function. + Args: + period: Period in seconds for running the work repeatedly. + action: Action to be executed. + state: [Optional] state to be given to the action function. - Returns: - The disposable object used to cancel the scheduled action - (best effort). + Returns: + The disposable object used to cancel the scheduled action + (best effort). """ return self._gtk_schedule(period, action, state=state, periodic=True) diff --git a/rx/scheduler/mainloop/pygamescheduler.py b/rx/scheduler/mainloop/pygamescheduler.py index 7098c4bf0..81963f3d9 100644 --- a/rx/scheduler/mainloop/pygamescheduler.py +++ b/rx/scheduler/mainloop/pygamescheduler.py @@ -1,15 +1,15 @@ import logging import threading +from typing import Any, Optional, TypeVar -from typing import Any, Optional - -from rx.core import typing +from rx.core import abc, typing from rx.internal import PriorityQueue from rx.internal.constants import DELTA_ZERO -from ..scheduleditem import ScheduledItem from ..periodicscheduler import PeriodicScheduler +from ..scheduleditem import ScheduledItem +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -35,10 +35,7 @@ def __init__(self, pygame: Any): self._lock = threading.Lock() self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -53,11 +50,12 @@ def schedule(self, log.debug("PyGameScheduler.schedule(state=%s)", state) return self.schedule_absolute(self.now, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: duetime: Relative time after which to execute the action. @@ -72,11 +70,12 @@ def schedule_relative(self, duetime = max(DELTA_ZERO, self.to_timedelta(duetime)) return self.schedule_absolute(self.now + duetime, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/mainloop/qtscheduler.py b/rx/scheduler/mainloop/qtscheduler.py index c9c239389..f7cf1710e 100644 --- a/rx/scheduler/mainloop/qtscheduler.py +++ b/rx/scheduler/mainloop/qtscheduler.py @@ -1,13 +1,14 @@ import logging - from datetime import timedelta -from typing import Any, Optional, Set +from typing import Any, Optional, Set, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") log = logging.getLogger(__name__) @@ -27,10 +28,7 @@ def __init__(self, qtcore: Any): timer_class: Any = self._qtcore.QTimer self._periodic_timers: Set[timer_class] = set() - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -43,11 +41,12 @@ def schedule(self, """ return self.schedule_relative(0.0, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -78,11 +77,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -98,21 +98,22 @@ def schedule_absolute(self, delta: timedelta = self.to_datetime(duetime) - self.now return self.schedule_relative(delta, action, state=state) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction, + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. - Args: - period: Period in seconds for running the work repeatedly. - action: Action to be executed. - state: [Optional] state to be given to the action function. + Args: + period: Period in seconds for running the work repeatedly. + action: Action to be executed. + state: [Optional] state to be given to the action function. - Returns: - The disposable object used to cancel the scheduled action - (best effort). + Returns: + The disposable object used to cancel the scheduled action + (best effort). """ msecs = max(0, int(self.to_seconds(period) * 1000.0)) sad = SingleAssignmentDisposable() diff --git a/rx/scheduler/mainloop/tkinterscheduler.py b/rx/scheduler/mainloop/tkinterscheduler.py index eeca81a56..75bd43fd1 100644 --- a/rx/scheduler/mainloop/tkinterscheduler.py +++ b/rx/scheduler/mainloop/tkinterscheduler.py @@ -1,10 +1,13 @@ -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") + class TkinterScheduler(PeriodicScheduler): """A scheduler that schedules work via the Tkinter main event loop. @@ -23,10 +26,7 @@ def __init__(self, root: Any) -> None: super().__init__() self._root = root - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -40,11 +40,12 @@ def schedule(self, return self.schedule_relative(0.0, action, state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -70,11 +71,12 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/mainloop/wxscheduler.py b/rx/scheduler/mainloop/wxscheduler.py index 08080a6b2..0e3923b15 100644 --- a/rx/scheduler/mainloop/wxscheduler.py +++ b/rx/scheduler/mainloop/wxscheduler.py @@ -1,12 +1,13 @@ import logging +from typing import Any, Optional, Set, TypeVar, cast -from typing import cast, Any, Optional, Set - -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from ..periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") log = logging.getLogger("Rx") @@ -27,7 +28,6 @@ def __init__(self, wx: Any) -> None: timer_class: Any = self._wx.Timer class Timer(timer_class): - def __init__(self, callback) -> None: super().__init__() self.callback = callback @@ -47,12 +47,13 @@ def cancel_all(self) -> None: for timer in self._timers: timer.Stop() - def _wxtimer_schedule(self, - time: typing.AbsoluteOrRelativeTime, - action: typing.ScheduledSingleOrPeriodicAction, - state: Optional[typing.TState] = None, - periodic: bool = False - ) -> typing.Disposable: + def _wxtimer_schedule( + self, + time: typing.AbsoluteOrRelativeTime, + action: typing.ScheduledSingleOrPeriodicAction, + state: Optional[_TState] = None, + periodic: bool = False, + ) -> abc.DisposableBase: scheduler = self sad = SingleAssignmentDisposable() @@ -62,17 +63,14 @@ def interval() -> None: if periodic: state = cast(typing.ScheduledPeriodicAction, action)(state) else: - sad.disposable = cast(typing.ScheduledAction, action)(scheduler, state) + sad.disposable = cast(typing.ScheduledAction[_TState], action)(scheduler, state) msecs = max(1, int(self.to_seconds(time) * 1000.0)) # Must be non-zero log.debug("timeout wx: %s", msecs) timer = self._timer_class(interval) - timer.Start( - msecs, - self._wx.TIMER_CONTINUOUS if periodic else self._wx.TIMER_ONE_SHOT - ) + timer.Start(msecs, self._wx.TIMER_CONTINUOUS if periodic else self._wx.TIMER_ONE_SHOT) self._timers.add(timer) def dispose() -> None: @@ -81,10 +79,7 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -98,11 +93,12 @@ def schedule(self, return self._wxtimer_schedule(0.0, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -116,11 +112,12 @@ def schedule_relative(self, """ return self._wxtimer_schedule(duetime, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -136,21 +133,22 @@ def schedule_absolute(self, duetime = self.to_datetime(duetime) return self._wxtimer_schedule(duetime - self.now, action, state=state) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction, + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. - Args: - period: Period in seconds for running the work repeatedly. - action: Action to be executed. - state: [Optional] state to be given to the action function. + Args: + period: Period in seconds for running the work repeatedly. + action: Action to be executed. + state: [Optional] state to be given to the action function. - Returns: - The disposable object used to cancel the scheduled action - (best effort). + Returns: + The disposable object used to cancel the scheduled action + (best effort). """ return self._wxtimer_schedule(period, action, state=state, periodic=True) diff --git a/rx/scheduler/newthreadscheduler.py b/rx/scheduler/newthreadscheduler.py index 5edfe4c2c..ea9f11b92 100644 --- a/rx/scheduler/newthreadscheduler.py +++ b/rx/scheduler/newthreadscheduler.py @@ -1,32 +1,28 @@ import logging import threading - from datetime import datetime -from typing import Optional +from typing import Optional, TypeVar -from rx.core import typing +from rx.core import abc, typing from rx.disposable import Disposable from rx.internal.concurrency import default_thread_factory from .eventloopscheduler import EventLoopScheduler from .periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") -log = logging.getLogger('Rx') +log = logging.getLogger("Rx") class NewThreadScheduler(PeriodicScheduler): - """Creates an object that schedules each unit of work on a separate thread. - """ + """Creates an object that schedules each unit of work on a separate thread.""" def __init__(self, thread_factory: Optional[typing.StartableFactory] = None) -> None: super().__init__() self.thread_factory: typing.StartableFactory = thread_factory or default_thread_factory - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -41,11 +37,9 @@ def schedule(self, scheduler = EventLoopScheduler(thread_factory=self.thread_factory, exit_if_empty=True) return scheduler.schedule(action, state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -61,11 +55,9 @@ def schedule_relative(self, scheduler = EventLoopScheduler(thread_factory=self.thread_factory, exit_if_empty=True) return scheduler.schedule_relative(duetime, action, state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -81,11 +73,12 @@ def schedule_absolute(self, dt = self.to_datetime(duetime) return self.schedule_relative(dt - self.now, action, state=state) - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work. Args: diff --git a/rx/scheduler/periodicscheduler.py b/rx/scheduler/periodicscheduler.py index 6d35cd1a9..12cc697c0 100644 --- a/rx/scheduler/periodicscheduler.py +++ b/rx/scheduler/periodicscheduler.py @@ -1,23 +1,26 @@ from abc import abstractmethod from datetime import datetime -from typing import Optional +from typing import Optional, TypeVar -from rx.core import typing +from rx.core import abc, typing from rx.disposable import Disposable, MultipleAssignmentDisposable from .scheduler import Scheduler +_TState = TypeVar("_TState") -class PeriodicScheduler(Scheduler, typing.PeriodicScheduler): + +class PeriodicScheduler(Scheduler, abc.PeriodicSchedulerBase): """Base class for the various periodic scheduler implementations in this package as well as the mainloop sub-package. """ - def schedule_periodic(self, - period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_periodic( + self, + period: typing.RelativeTime, + action: typing.ScheduledPeriodicAction[_TState], + state: Optional[_TState] = None, + ) -> abc.DisposableBase: """Schedules a periodic piece of work. Args: @@ -35,9 +38,7 @@ def schedule_periodic(self, disp: MultipleAssignmentDisposable = MultipleAssignmentDisposable() seconds: float = self.to_seconds(period) - def periodic(scheduler: typing.Scheduler, - state: Optional[typing.TState] = None - ) -> Optional[Disposable]: + def periodic(scheduler: abc.SchedulerBase, state: Optional[_TState] = None) -> Optional[Disposable]: if disp.is_disposed: return None @@ -58,10 +59,7 @@ def periodic(scheduler: typing.Scheduler, return disp @abstractmethod - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -76,11 +74,9 @@ def schedule(self, return NotImplemented @abstractmethod - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -96,11 +92,9 @@ def schedule_relative(self, return NotImplemented @abstractmethod - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/scheduleditem.py b/rx/scheduler/scheduleditem.py index 6b5386270..d177c3f94 100644 --- a/rx/scheduler/scheduleditem.py +++ b/rx/scheduler/scheduleditem.py @@ -1,23 +1,21 @@ from datetime import datetime -from typing import Any, Optional +from typing import Any, Optional, TypeVar -from rx.core import typing +from rx.core import abc from rx.disposable import SingleAssignmentDisposable from .scheduler import Scheduler +_TState = TypeVar("_TState") -class ScheduledItem(object): - def __init__(self, - scheduler: Scheduler, - state: Optional[Any], - action: typing.ScheduledAction, - duetime: datetime - ) -> None: +class ScheduledItem(object): + def __init__( + self, scheduler: Scheduler, state: Optional[_TState], action: abc.ScheduledAction[_TState], duetime: datetime + ) -> None: self.scheduler: Scheduler = scheduler self.state: Optional[Any] = state - self.action: typing.ScheduledAction = action + self.action: abc.ScheduledAction[_TState] = action self.duetime: datetime = duetime self.disposable: SingleAssignmentDisposable = SingleAssignmentDisposable() @@ -34,10 +32,10 @@ def cancel(self) -> None: def is_cancelled(self) -> bool: return self.disposable.is_disposed - def __lt__(self, other: 'ScheduledItem') -> bool: + def __lt__(self, other: "ScheduledItem") -> bool: return self.duetime < other.duetime - def __gt__(self, other: 'ScheduledItem') -> bool: + def __gt__(self, other: "ScheduledItem") -> bool: return self.duetime > other.duetime def __eq__(self, other: Any) -> bool: diff --git a/rx/scheduler/scheduler.py b/rx/scheduler/scheduler.py index 886e28532..c5fac1188 100644 --- a/rx/scheduler/scheduler.py +++ b/rx/scheduler/scheduler.py @@ -1,14 +1,16 @@ from abc import abstractmethod from datetime import datetime, timedelta -from typing import Optional +from typing import Optional, TypeVar -from rx.core import typing +from rx.core import abc, typing from rx.disposable import Disposable from rx.internal.basic import default_now from rx.internal.constants import UTC_ZERO +_TState = TypeVar("_TState") -class Scheduler(typing.Scheduler): + +class Scheduler(abc.SchedulerBase): """Base class for the various scheduler implementations in this package as well as the mainloop sub-package. This does not include an implementation of schedule_periodic, refer to PeriodicScheduler. @@ -27,10 +29,7 @@ def now(self) -> datetime: return default_now() @abstractmethod - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -45,11 +44,9 @@ def schedule(self, return NotImplemented @abstractmethod - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -65,11 +62,9 @@ def schedule_relative(self, return NotImplemented @abstractmethod - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -84,10 +79,9 @@ def schedule_absolute(self, return NotImplemented - def invoke_action(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def invoke_action( + self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Invoke the given given action. This is typically called by instances of ScheduledItem. @@ -101,7 +95,7 @@ def invoke_action(self, """ ret = action(self, state) - if isinstance(ret, typing.Disposable): + if isinstance(ret, abc.DisposableBase): return ret return Disposable() diff --git a/rx/scheduler/threadpoolscheduler.py b/rx/scheduler/threadpoolscheduler.py index c4871eaca..b41586ce1 100644 --- a/rx/scheduler/threadpoolscheduler.py +++ b/rx/scheduler/threadpoolscheduler.py @@ -1,8 +1,7 @@ from concurrent.futures import Future, ThreadPoolExecutor from typing import Optional -from rx.core.abc import Startable -from rx.core import typing +from rx.core import abc, typing from .newthreadscheduler import NewThreadScheduler @@ -10,12 +9,10 @@ class ThreadPoolScheduler(NewThreadScheduler): """A scheduler that schedules work via the thread pool.""" - class ThreadPoolThread(Startable): + class ThreadPoolThread(abc.StartableBase): """Wraps a concurrent future as a thread.""" - def __init__(self, - executor: ThreadPoolExecutor, - target: typing.StartableTarget): + def __init__(self, executor: ThreadPoolExecutor, target: typing.StartableTarget): self.executor: ThreadPoolExecutor = executor self.target: typing.StartableTarget = target self.future: Optional[Future] = None @@ -34,4 +31,3 @@ def thread_factory(target: typing.StartableTarget): return self.ThreadPoolThread(self.executor, target) super().__init__(thread_factory) - diff --git a/rx/scheduler/timeoutscheduler.py b/rx/scheduler/timeoutscheduler.py index eeb201030..ec957a78f 100644 --- a/rx/scheduler/timeoutscheduler.py +++ b/rx/scheduler/timeoutscheduler.py @@ -1,21 +1,24 @@ from threading import Lock, Timer -from typing import MutableMapping, Optional +from typing import MutableMapping, Optional, TypeVar from weakref import WeakKeyDictionary -from rx.core import typing -from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable +from rx.core import abc, typing +from rx.disposable import (CompositeDisposable, Disposable, + SingleAssignmentDisposable) from .periodicscheduler import PeriodicScheduler +_TState = TypeVar("_TState") + class TimeoutScheduler(PeriodicScheduler): """A scheduler that schedules work via a timed callback.""" _lock = Lock() - _global: MutableMapping[type, 'TimeoutScheduler'] = WeakKeyDictionary() + _global: MutableMapping[type, "TimeoutScheduler"] = WeakKeyDictionary() @classmethod - def singleton(cls) -> 'TimeoutScheduler': + def singleton(cls) -> "TimeoutScheduler": with TimeoutScheduler._lock: try: self = TimeoutScheduler._global[cls] @@ -24,13 +27,10 @@ def singleton(cls) -> 'TimeoutScheduler': TimeoutScheduler._global[cls] = self return self - def __new__(cls) -> 'TimeoutScheduler': + def __new__(cls) -> "TimeoutScheduler": return cls.singleton() - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -56,11 +56,9 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -91,11 +89,9 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: diff --git a/rx/scheduler/trampoline.py b/rx/scheduler/trampoline.py index 78c1f6bba..c6e585187 100644 --- a/rx/scheduler/trampoline.py +++ b/rx/scheduler/trampoline.py @@ -7,8 +7,7 @@ from .scheduleditem import ScheduledItem -class Trampoline(object): - +class Trampoline: def __init__(self): self._idle: bool = True self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() diff --git a/rx/scheduler/trampolinescheduler.py b/rx/scheduler/trampolinescheduler.py index 7c16e0881..b86a83789 100644 --- a/rx/scheduler/trampolinescheduler.py +++ b/rx/scheduler/trampolinescheduler.py @@ -1,16 +1,16 @@ import logging +from typing import Optional, TypeVar -from typing import Optional - -from rx.core import typing +from rx.core import abc, typing +from rx.core.abc.scheduler import ScheduledAction from rx.internal.constants import DELTA_ZERO from .scheduleditem import ScheduledItem from .scheduler import Scheduler from .trampoline import Trampoline - -log = logging.getLogger('Rx') +_TState = TypeVar("_TState") +log = logging.getLogger("Rx") class TrampolineScheduler(Scheduler): @@ -25,17 +25,13 @@ class TrampolineScheduler(Scheduler): """ def __init__(self) -> None: - """Creates a scheduler that bounces its work off the trampoline.""" self._tramp = Trampoline() def get_trampoline(self) -> Trampoline: return self._tramp - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -49,11 +45,9 @@ def schedule(self, return self.schedule_absolute(self.now, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -69,11 +63,9 @@ def schedule_relative(self, duetime = max(DELTA_ZERO, self.to_timedelta(duetime)) return self.schedule_absolute(self.now + duetime, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -88,7 +80,7 @@ def schedule_absolute(self, dt = self.to_datetime(duetime) if dt > self.now: - log.warning('Do not schedule blocking work!') + log.warning("Do not schedule blocking work!") item: ScheduledItem = ScheduledItem(self, state, action, dt) self.get_trampoline().run(item) @@ -105,7 +97,7 @@ def schedule_required(self) -> bool: """ return self.get_trampoline().idle() - def ensure_trampoline(self, action): + def ensure_trampoline(self, action: ScheduledAction[_TState]): """Method for testing the TrampolineScheduler.""" if self.schedule_required(): diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index 12627533e..1d3a1f6c5 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -2,10 +2,10 @@ import threading from abc import abstractmethod from datetime import datetime -from typing import Any, Optional +from typing import Optional, TypeVar -from rx.internal import PriorityQueue, ArgumentOutOfRangeException -from rx.core import typing +from rx.core import abc, typing +from rx.internal import ArgumentOutOfRangeException, PriorityQueue from .periodicscheduler import PeriodicScheduler from .scheduleditem import ScheduledItem @@ -14,12 +14,14 @@ MAX_SPINNING = 100 +_TState = TypeVar("_TState") + class VirtualTimeScheduler(PeriodicScheduler): """Virtual Scheduler. This scheduler should work with either datetime/timespan or ticks as int/int""" - def __init__(self, initial_clock=0) -> None: + def __init__(self, initial_clock: int = 0) -> None: """Creates a new virtual time scheduler with the specified initial clock value. @@ -47,14 +49,11 @@ def now(self) -> datetime: Returns: The scheduler's current time, as a datetime instance. - """ + """ return self.to_datetime(self._clock) - def schedule(self, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -68,11 +67,9 @@ def schedule(self, return self.schedule_absolute(self._clock, action, state=state) - def schedule_relative(self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_relative( + self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. Args: @@ -88,11 +85,9 @@ def schedule_relative(self, time: typing.AbsoluteTime = self.add(self._clock, self.to_seconds(duetime)) return self.schedule_absolute(time, action, state=state) - def schedule_absolute(self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction, - state: Optional[typing.TState] = None - ) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. Args: @@ -230,10 +225,7 @@ def sleep(self, time: typing.RelativeTime) -> None: @classmethod @abstractmethod - def add(cls, - absolute: typing.AbsoluteTime, - relative: typing.RelativeTime - ) -> typing.AbsoluteTime: + def add(cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime) -> typing.AbsoluteTime: """Adds a relative time value to an absolute time value. Args: diff --git a/rx/subject/__init__.py b/rx/subject/__init__.py index d3cbaa154..563688b8f 100644 --- a/rx/subject/__init__.py +++ b/rx/subject/__init__.py @@ -1,4 +1,6 @@ -from .subject import Subject from .asyncsubject import AsyncSubject from .behaviorsubject import BehaviorSubject from .replaysubject import ReplaySubject +from .subject import Subject + +__all__ = ["Subject", "AsyncSubject", "BehaviorSubject", "ReplaySubject"] diff --git a/rx/subject/asyncsubject.py b/rx/subject/asyncsubject.py index 78b5bd84e..ca8d26ba0 100644 --- a/rx/subject/asyncsubject.py +++ b/rx/subject/asyncsubject.py @@ -1,13 +1,15 @@ -from typing import Any, Optional +from typing import Any, Optional, TypeVar +from rx.core import abc, typing from rx.disposable import Disposable -from rx.core import typing -from .subject import Subject from .innersubscription import InnerSubscription +from .subject import Subject + +_T = TypeVar("_T") -class AsyncSubject(Subject): +class AsyncSubject(Subject[_T]): """Represents the result of an asynchronous operation. The last value before the close notification, or the error received through on_error, is sent to all subscribed observers.""" @@ -18,13 +20,12 @@ def __init__(self) -> None: super().__init__() - self.value = None - self.has_value = False + self.value: Optional[_T] = None + self.has_value: bool = False - def _subscribe_core(self, - observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: + def _subscribe_core( + self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: with self.lock: self.check_disposed() if not self.is_stopped: @@ -45,7 +46,7 @@ def _subscribe_core(self, return Disposable() - def _on_next_core(self, value: Any) -> None: + def _on_next_core(self, value: _T) -> None: """Remember the value. Upon completion, the most recently received value will be passed on to all subscribed observers. diff --git a/rx/subject/behaviorsubject.py b/rx/subject/behaviorsubject.py index 9e20e7458..2f742d5da 100644 --- a/rx/subject/behaviorsubject.py +++ b/rx/subject/behaviorsubject.py @@ -2,8 +2,8 @@ from rx.disposable import Disposable -from .subject import Subject from .innersubscription import InnerSubscription +from .subject import Subject class BehaviorSubject(Subject): diff --git a/rx/subject/innersubscription.py b/rx/subject/innersubscription.py index 0af6161a7..05d826957 100644 --- a/rx/subject/innersubscription.py +++ b/rx/subject/innersubscription.py @@ -1,10 +1,14 @@ import threading +from typing import TypeVar -from rx.core import typing +from rx.core import abc +from rx.core.abc.observable import ObservableBase +_T = TypeVar("_T") -class InnerSubscription(typing.Disposable): - def __init__(self, subject, observer): + +class InnerSubscription(abc.DisposableBase): + def __init__(self, subject: abc.SubjectBase[_T], observer: ObservableBase[_T]): self.subject = subject self.observer = observer diff --git a/rx/subject/replaysubject.py b/rx/subject/replaysubject.py index 87f7f753a..3206f5230 100644 --- a/rx/subject/replaysubject.py +++ b/rx/subject/replaysubject.py @@ -1,18 +1,18 @@ import sys +from datetime import datetime, timedelta +from typing import Any, List, NamedTuple, Optional, TypeVar, cast -from datetime import datetime -from typing import cast, Any, Optional, List, NamedTuple -from datetime import timedelta - -from rx.core import typing -from rx.scheduler import CurrentThreadScheduler +from rx.core import Observer, abc, typing from rx.core.observer.scheduledobserver import ScheduledObserver +from rx.scheduler import CurrentThreadScheduler from .subject import Subject +_T = TypeVar("_T") + -class RemovableDisposable(typing.Disposable): - def __init__(self, subject, observer): +class RemovableDisposable(abc.DisposableBase): + def __init__(self, subject: Subject[_T], observer: Observer[_T]): self.subject = subject self.observer = observer @@ -27,17 +27,18 @@ class QueueItem(NamedTuple): value: Any -class ReplaySubject(Subject): +class ReplaySubject(Subject[_T]): """Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies. """ - def __init__(self, - buffer_size: int = None, - window: typing.RelativeTime = None, - scheduler: Optional[typing.Scheduler] = None - ) -> None: + def __init__( + self, + buffer_size: Optional[int] = None, + window: Optional[typing.RelativeTime] = None, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> None: """Initializes a new instance of the ReplaySubject class with the specified buffer size, window and scheduler. @@ -54,10 +55,9 @@ def __init__(self, self.window = timedelta.max if window is None else self.scheduler.to_timedelta(window) self.queue: List[QueueItem] = [] - def _subscribe_core(self, - observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: + def _subscribe_core( + self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: so = ScheduledObserver(self.scheduler, observer) subscription = RemovableDisposable(self, so) @@ -97,7 +97,7 @@ def _on_next_core(self, value: Any) -> None: observer.on_next(value) for observer in observers: - cast(ScheduledObserver, observer).ensure_active() + cast(ScheduledObserver[_T], observer).ensure_active() def _on_error_core(self, error: Exception) -> None: """Notifies all subscribed observers with the exception.""" @@ -111,7 +111,7 @@ def _on_error_core(self, error: Exception) -> None: for observer in observers: observer.on_error(error) - cast(ScheduledObserver, observer).ensure_active() + cast(ScheduledObserver[_T], observer).ensure_active() def _on_completed_core(self) -> None: """Notifies all subscribed observers of the end of the sequence.""" @@ -124,7 +124,7 @@ def _on_completed_core(self) -> None: for observer in observers: observer.on_completed() - cast(ScheduledObserver, observer).ensure_active() + cast(ScheduledObserver[_T], observer).ensure_active() def dispose(self) -> None: """Releases all resources used by the current instance of the diff --git a/rx/subject/subject.py b/rx/subject/subject.py index db9d9d77d..de4bc9b61 100644 --- a/rx/subject/subject.py +++ b/rx/subject/subject.py @@ -1,14 +1,16 @@ import threading -from typing import Any, List, Optional +from typing import List, Optional, TypeVar +from rx.core import Observable, Observer, abc from rx.disposable import Disposable -from rx.core import Observable, Observer, typing from rx.internal import DisposedException from .innersubscription import InnerSubscription +_T = TypeVar("_T") -class Subject(Observable, Observer, typing.Subject): + +class Subject(Observable[_T], Observer[_T], abc.SubjectBase[_T]): """Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed observers. @@ -18,7 +20,7 @@ def __init__(self) -> None: super().__init__() self.is_disposed = False - self.observers: List[typing.Observer] = [] + self.observers: List[abc.ObserverBase[_T]] = [] self.exception: Optional[Exception] = None self.lock = threading.RLock() @@ -27,10 +29,9 @@ def check_disposed(self) -> None: if self.is_disposed: raise DisposedException() - def _subscribe_core(self, - observer: typing.Observer, - scheduler: Optional[typing.Scheduler] = None - ) -> typing.Disposable: + def _subscribe_core( + self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: with self.lock: self.check_disposed() if not self.is_stopped: @@ -43,7 +44,7 @@ def _subscribe_core(self, observer.on_completed() return Disposable() - def on_next(self, value: Any) -> None: + def on_next(self, value: _T) -> None: """Notifies all subscribed observers with the value. Args: @@ -54,7 +55,7 @@ def on_next(self, value: Any) -> None: self.check_disposed() super().on_next(value) - def _on_next_core(self, value: Any) -> None: + def _on_next_core(self, value: _T) -> None: with self.lock: observers = self.observers.copy() diff --git a/rx/testing/__init__.py b/rx/testing/__init__.py index 752e161c7..392457e77 100644 --- a/rx/testing/__init__.py +++ b/rx/testing/__init__.py @@ -1,4 +1,5 @@ -from .testscheduler import TestScheduler -from .reactivetest import OnNextPredicate, OnErrorPredicate, ReactiveTest, is_prime from .mockdisposable import MockDisposable +from .reactivetest import (OnErrorPredicate, OnNextPredicate, ReactiveTest, + is_prime) from .recorded import Recorded +from .testscheduler import TestScheduler diff --git a/rx/testing/coldobservable.py b/rx/testing/coldobservable.py index 59ae054b1..6535cf22f 100644 --- a/rx/testing/coldobservable.py +++ b/rx/testing/coldobservable.py @@ -1,14 +1,15 @@ -from typing import List +from typing import List, Optional, TypeVar -from rx.disposable import Disposable, CompositeDisposable -from rx.core import Observable, typing +from rx.core import Observable, abc +from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import VirtualTimeScheduler from .subscription import Subscription +_T = TypeVar("_T") -class ColdObservable(Observable): +class ColdObservable(Observable[_T]): def __init__(self, scheduler: VirtualTimeScheduler, messages) -> None: super().__init__() @@ -16,7 +17,7 @@ def __init__(self, scheduler: VirtualTimeScheduler, messages) -> None: self.messages = messages self.subscriptions: List[Subscription] = [] - def _subscribe_core(self, observer=None, scheduler=None) -> typing.Disposable: + def _subscribe_core(self, observer=None, scheduler: Optional[abc.SubjectBase] = None) -> abc.DisposableBase: self.subscriptions.append(Subscription(self.scheduler.clock)) index = len(self.subscriptions) - 1 disp = CompositeDisposable() @@ -25,6 +26,7 @@ def get_action(notification): def action(scheduler, state): notification.accept(observer) return Disposable() + return action for message in self.messages: diff --git a/rx/testing/hotobservable.py b/rx/testing/hotobservable.py index d4cb78760..d755c59b5 100644 --- a/rx/testing/hotobservable.py +++ b/rx/testing/hotobservable.py @@ -1,29 +1,33 @@ -from typing import List +from typing import Any, List, Optional, TypeVar +from rx.core import Observable, abc +from rx.core.notification import Notification from rx.disposable import Disposable -from rx.core import Observable, typing from rx.scheduler import VirtualTimeScheduler from .recorded import Recorded from .subscription import Subscription +_T = TypeVar("_T") -class HotObservable(Observable): + +class HotObservable(Observable[_T]): def __init__(self, scheduler: VirtualTimeScheduler, messages: List[Recorded]) -> None: super().__init__() self.scheduler: VirtualTimeScheduler = scheduler self.messages = messages self.subscriptions: List[Subscription] = [] - self.observers: List[typing.Observer] = [] + self.observers: List[abc.ObserverBase[_T]] = [] observable = self - def get_action(notification): - def action(scheduler, state): + def get_action(notification: Notification): + def action(scheduler: abc.SchedulerBase, state: Any): for observer in observable.observers[:]: notification.accept(observer) return Disposable() + return action for message in self.messages: @@ -33,7 +37,7 @@ def action(scheduler, state): action = get_action(notification) scheduler.schedule_absolute(message.time, action) - def _subscribe_core(self, observer=None, scheduler=None) -> typing.Disposable: + def _subscribe_core(self, observer=None, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: self.observers.append(observer) self.subscriptions.append(Subscription(self.scheduler.clock)) index = len(self.subscriptions) - 1 diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index 0e5deb846..dd2ab616b 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -1,15 +1,15 @@ -from typing import List, Tuple, Union, Dict from collections import namedtuple from contextlib import contextmanager +from typing import Dict, List, Tuple, Union from warnings import warn import rx from rx.core import Observable from rx.core.notification import Notification -from rx.scheduler import NewThreadScheduler -from rx.core.typing import Callable, RelativeTime -from rx.testing import TestScheduler, Recorded, ReactiveTest from rx.core.observable.marbles import parse +from rx.core.typing import Callable, RelativeTime +from rx.scheduler import NewThreadScheduler +from rx.testing import ReactiveTest, Recorded, TestScheduler new_thread_scheduler = NewThreadScheduler() diff --git a/rx/testing/mockobserver.py b/rx/testing/mockobserver.py index c9338746f..95643cf63 100644 --- a/rx/testing/mockobserver.py +++ b/rx/testing/mockobserver.py @@ -1,14 +1,15 @@ -from typing import Any, List +from typing import Any, List, TypeVar -from rx.core.typing import Observer -from rx.core.notification import OnNext, OnError, OnCompleted +from rx.core.abc import ObserverBase +from rx.core.notification import OnCompleted, OnError, OnNext from rx.scheduler import VirtualTimeScheduler from .recorded import Recorded +_T = TypeVar("_T") -class MockObserver(Observer): +class MockObserver(ObserverBase[_T]): def __init__(self, scheduler: VirtualTimeScheduler) -> None: self.scheduler: VirtualTimeScheduler = scheduler self.messages: List[Recorded] = [] diff --git a/rx/testing/reactivetest.py b/rx/testing/reactivetest.py index 92b0497c2..dd8e2bec1 100644 --- a/rx/testing/reactivetest.py +++ b/rx/testing/reactivetest.py @@ -1,9 +1,9 @@ -from typing import Any - import math import types +from typing import Any + +from rx.core.notification import OnCompleted, OnError, OnNext -from rx.core.notification import OnNext, OnError, OnCompleted from .recorded import Recorded from .subscription import Subscription diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index d8af88985..fa08ecda3 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,4 +1,5 @@ from typing import Any + from rx.internal.basic import default_comparer diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index decf97139..793e6bc59 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -1,8 +1,8 @@ -from typing import Callable, Any +from typing import Any, Callable, TypeVar import rx +from rx.core import Observable, abc, typing from rx.disposable import Disposable -from rx.core import Observable, typing from rx.scheduler import VirtualTimeScheduler from .coldobservable import ColdObservable @@ -10,6 +10,8 @@ from .mockobserver import MockObserver from .reactivetest import ReactiveTest +_TState = TypeVar("_TState") + class TestScheduler(VirtualTimeScheduler): """Test time scheduler used for testing applications and libraries @@ -18,7 +20,9 @@ class TestScheduler(VirtualTimeScheduler): __test__ = False - def schedule_absolute(self, duetime: typing.AbsoluteTime, action: Callable, state: Any = None) -> typing.Disposable: + def schedule_absolute( + self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: _TState = None + ) -> abc.DisposableBase: """Schedules an action to be executed at the specified virtual time. @@ -76,18 +80,21 @@ def action_create(scheduler, state): """Called at create time. Defaults to 100""" source[0] = create() return Disposable() + self.schedule_absolute(created, action_create) def action_subscribe(scheduler, state): """Called at subscribe time. Defaults to 200""" subscription[0] = source[0].subscribe(observer, scheduler=scheduler) return Disposable() + self.schedule_absolute(subscribed, action_subscribe) def action_dispose(scheduler, state): """Called at dispose time. Defaults to 1000""" subscription[0].dispose() return Disposable() + self.schedule_absolute(disposed, action_dispose) super().start() diff --git a/tests/test_core/test_notification.py b/tests/test_core/test_notification.py index 6645289fa..e0890d409 100644 --- a/tests/test_core/test_notification.py +++ b/tests/test_core/test_notification.py @@ -1,4 +1,4 @@ -from rx.core.typing import Observer +from rx.core.abc import ObserverBase from rx.testing import TestScheduler, ReactiveTest from rx.core.notification import OnNext, OnError, OnCompleted @@ -13,7 +13,7 @@ def test_on_next_ctor_and_props(): n = OnNext(42) - assert 'N' == n.kind + assert "N" == n.kind assert n.has_value assert 42 == n.value assert not hasattr(n, "exception") @@ -40,7 +40,7 @@ def test_on_next_tostring(): assert "42" in str(n1) -class CheckOnNextObserver(Observer): +class CheckOnNextObserver(ObserverBase): def __init__(self): super(CheckOnNextObserver, self).__init__() @@ -56,6 +56,7 @@ def on_error(self, error): def on_completed(self): def func(): raise NotImplementedError + return func @@ -66,7 +67,7 @@ def test_on_next_accept_observer(): assert con.value == 42 -class AcceptObserver(Observer): +class AcceptObserver(ObserverBase): def __init__(self, on_next, on_error, on_completed): self._on_next = on_next self._on_error = on_error @@ -87,25 +88,31 @@ def test_on_next_accept_observer_with_result(): def on_next(x): return "OK" + def on_error(err): assert False + def on_completed(): assert False res = n1.accept(AcceptObserver(on_next, on_error, on_completed)) - assert 'OK' == res + assert "OK" == res def test_on_next_accept_action(): obs = [False] n1 = OnNext(42) + def on_next(x): obs[0] = True return obs[0] + def on_error(err): assert False + def on_completed(): assert False + n1.accept(on_next, on_error, on_completed) assert obs[0] @@ -116,26 +123,28 @@ def test_on_next_accept_action_with_result(): def on_next(x): return "OK" + def on_error(err): assert False + def on_completed(): assert False res = n1.accept(on_next, on_error, on_completed) - assert 'OK' == res + assert "OK" == res def test_throw_ctor_and_props(): - e = 'e' + e = "e" n = OnError(e) - assert 'E'== n.kind + assert "E" == n.kind assert not n.has_value assert e == n.exception def test_throw_equality(): - ex1 = 'ex1' - ex2 = 'ex2' + ex1 = "ex1" + ex2 = "ex2" n1 = OnError(ex1) n2 = OnError(ex1) n3 = OnError(ex2) @@ -151,13 +160,13 @@ def test_throw_equality(): def test_throw_tostring(): - ex = 'ex' + ex = "ex" n1 = OnError(ex) assert "OnError" in str(n1) assert "ex" in str(n1) -class CheckOnErrorObserver(Observer): +class CheckOnErrorObserver(ObserverBase): def __init__(self): super(CheckOnErrorObserver, self).__init__() @@ -174,7 +183,7 @@ def on_completed(self): def test_throw_accept_observer(): - ex = 'ex' + ex = "ex" obs = CheckOnErrorObserver() n1 = OnError(ex) n1.accept(obs) @@ -182,12 +191,13 @@ def test_throw_accept_observer(): def test_throw_accept_observer_with_result(): - ex = 'ex' + ex = "ex" n1 = OnError(ex) def on_next(x): assert False return None + def on_error(ex): return "OK" @@ -196,20 +206,22 @@ def on_completed(): return None res = n1.accept(AcceptObserver(on_next, on_error, on_completed)) - assert 'OK' == res + assert "OK" == res def test_throw_accept_action(): - ex = 'ex' + ex = "ex" obs = [False] n1 = OnError(ex) def on_next(x): assert False return None + def on_error(ex): obs[0] = True return obs[0] + def on_completed(): assert False return None @@ -219,25 +231,27 @@ def on_completed(): def test_throw_accept_action_with_result(): - ex = 'ex' + ex = "ex" n1 = OnError(ex) def on_next(x): assert False return None + def on_error(ex): return "OK" + def on_completed(): assert False return None res = n1.accept(on_next, on_error, on_completed) - assert 'OK' == res + assert "OK" == res def test_close_ctor_and_props(): n = OnCompleted() - assert 'C' == n.kind + assert "C" == n.kind assert not n.has_value assert not hasattr(n, "exception") @@ -256,10 +270,10 @@ def test_close_equality(): def test_close_tostring(): n1 = OnCompleted() - assert 'OnCompleted' in str(n1) + assert "OnCompleted" in str(n1) -class CheckOnCompletedObserver(Observer): +class CheckOnCompletedObserver(ObserverBase): def __init__(self): super(CheckOnCompletedObserver, self).__init__() @@ -288,14 +302,16 @@ def test_close_accept_observer_with_result(): def on_next(x): assert False return None + def on_error(err): assert False return None + def on_completed(): return "OK" res = n1.accept(AcceptObserver(on_next, on_error, on_completed)) - assert 'OK' == res + assert "OK" == res def test_close_accept_action(): @@ -305,9 +321,11 @@ def test_close_accept_action(): def on_next(x): assert False return None + def on_error(ex): assert False return None + def on_completed(): obs[0] = True return obs[0] @@ -322,14 +340,16 @@ def test_close_accept_action_with_result(): def on_next(x): assert False return None + def on_error(ex): assert False return None + def on_completed(): return "OK" res = n1.accept(on_next, on_error, on_completed) - assert 'OK' == res + assert "OK" == res def test_to_observable_empty(): @@ -353,7 +373,7 @@ def create(): def test_to_observable_on_error(): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() def create(): diff --git a/tests/test_observable/test_connectableobservable.py b/tests/test_observable/test_connectableobservable.py index 8ab7a4e2d..7fc9c4f75 100644 --- a/tests/test_observable/test_connectableobservable.py +++ b/tests/test_observable/test_connectableobservable.py @@ -3,7 +3,7 @@ import rx from rx import operators as ops from rx.core import Observable -from rx.core.typing import Observer +from rx.core.abc import ObserverBase from rx.testing import TestScheduler, ReactiveTest from rx.subject import Subject from rx.core import ConnectableObservable @@ -17,8 +17,7 @@ created = ReactiveTest.created -class MySubject(Observable, Observer): - +class MySubject(Observable, ObserverBase): def __init__(self): super(MySubject, self).__init__() @@ -35,8 +34,9 @@ class Duck: def __init__(self, this): self.this = this - def dispose(self): + def dispose(self) -> None: self.this.disposed = True + return Duck(self) def dispose_on(self, value, disposable): @@ -55,7 +55,6 @@ def on_completed(self): class TestConnectableObservable(unittest.TestCase): - def test_connectable_observable_creation(self): y = [0] @@ -64,6 +63,7 @@ def test_connectable_observable_creation(self): def on_next(x): y[0] = x + co2.subscribe(on_next=on_next) self.assertNotEqual(1, y[0]) @@ -74,11 +74,7 @@ def test_connectable_observable_connected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250) + on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) ) subject = MySubject() @@ -88,22 +84,13 @@ def test_connectable_observable_connected(self): res = scheduler.start(lambda: conn) - assert res.messages == [ - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250)] + assert res.messages == [on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250)] def test_connectable_observable_not_connected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250) + on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) ) subject = MySubject() @@ -118,11 +105,7 @@ def test_connectable_observable_disconnected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250) + on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) ) subject = MySubject() @@ -139,11 +122,7 @@ def test_connectable_observable_disconnect_future(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250) + on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) ) subject = MySubject() @@ -153,10 +132,7 @@ def test_connectable_observable_disconnect_future(self): res = scheduler.start(lambda: conn) - assert res.messages == [ - on_next(210, 1), - on_next(220, 2), - on_next(230, 3)] + assert res.messages == [on_next(210, 1), on_next(220, 2), on_next(230, 3)] def test_connectable_observable_multiple_non_overlapped_connections(self): scheduler = TestScheduler() @@ -171,7 +147,7 @@ def test_connectable_observable_multiple_non_overlapped_connections(self): on_next(270, 7), on_next(280, 8), on_next(290, 9), - on_completed(300) + on_completed(300), ) subject = Subject() @@ -182,69 +158,72 @@ def test_connectable_observable_multiple_non_overlapped_connections(self): def action10(scheduler, state): c1[0] = conn.connect(scheduler) + scheduler.schedule_absolute(225, action10) def action11(scheduler, state): c1[0].dispose() + scheduler.schedule_absolute(241, action11) def action12(scheduler, state): c1[0].dispose() # idempotency test + scheduler.schedule_absolute(245, action12) def action13(scheduler, state): c1[0].dispose() # idempotency test + scheduler.schedule_absolute(251, action13) def action14(scheduler, state): c1[0].dispose() # idempotency test + scheduler.schedule_absolute(260, action14) c2 = [None] def action20(scheduler, state): c2[0] = conn.connect(scheduler) + scheduler.schedule_absolute(249, action20) def action21(scheduler, state): c2[0].dispose() + scheduler.schedule_absolute(255, action21) def action22(scheduler, state): c2[0].dispose() # idempotency test + scheduler.schedule_absolute(265, action22) def action23(scheduler, state): c2[0].dispose() # idempotency test + scheduler.schedule_absolute(280, action23) c3 = [None] def action30(scheduler, state): c3[0] = conn.connect(scheduler) + scheduler.schedule_absolute(275, action30) def action31(scheduler, state): c3[0].dispose() + scheduler.schedule_absolute(295, action31) res = scheduler.start(lambda: conn) - assert res.messages == [ - on_next(230, 3), - on_next(240, 4), - on_next(250, 5), - on_next(280, 8), - on_next(290, 9)] + assert res.messages == [on_next(230, 3), on_next(240, 4), on_next(250, 5), on_next(280, 8), on_next(290, 9)] - assert xs.subscriptions == [ - subscribe(225, 241), - subscribe(249, 255), - subscribe(275, 295)] + assert xs.subscriptions == [subscribe(225, 241), subscribe(249, 255), subscribe(275, 295)] def test_connectable_observable_forward_scheduler(self): scheduler = TestScheduler() - subscribe_scheduler = 'unknown' + subscribe_scheduler = "unknown" def subscribe(observer, scheduler=None): nonlocal subscribe_scheduler diff --git a/tests/test_observable/test_publish.py b/tests/test_observable/test_publish.py index a779dc2e0..58de03823 100644 --- a/tests/test_observable/test_publish.py +++ b/tests/test_observable/test_publish.py @@ -3,7 +3,7 @@ import rx from rx import operators as ops from rx.core import ConnectableObservable, Observable -from rx.core.typing import Observer +from rx.core.abc import ObserverBase from rx.testing import TestScheduler, ReactiveTest on_next = ReactiveTest.on_next @@ -24,8 +24,7 @@ def _raise(ex): raise RxException(ex) -class MySubject(Observable, Observer): - +class MySubject(Observable, ObserverBase): def __init__(self): super(MySubject, self).__init__() @@ -44,6 +43,7 @@ def __init__(self, this): def dispose(self): self.this.disposed = True + return Duck(self) def dispose_on(self, value, disposable): @@ -62,7 +62,6 @@ def on_completed(self): class TestPublish(unittest.TestCase): - def test_publish_cold_zip(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( @@ -74,34 +73,34 @@ def test_publish_cold_zip(self): on_next(270, 5), on_next(330, 6), on_next(340, 7), - on_completed(390) + on_completed(390), ) def create(): def mapper(ys): return ys.pipe( - ops.zip(ys), - ops.map(sum), - ) + ops.zip(ys), + ops.map(sum), + ) return xs.pipe(ops.publish(mapper=mapper)) + results = scheduler.start(create) - assert results.messages == [on_next(210, 6), - on_next(240, 8), - on_next(270, 10), - on_next(330, 12), - on_next(340, 14), on_completed(390)] + assert results.messages == [ + on_next(210, 6), + on_next(240, 8), + on_next(270, 10), + on_next(330, 12), + on_next(340, 14), + on_completed(390), + ] assert xs.subscriptions == [subscribe(200, 390)] def test_ref_count_connects_on_first(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250) + on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) ) subject = MySubject() conn = ConnectableObservable(xs, subject) @@ -111,12 +110,7 @@ def create(): res = scheduler.start(create) - assert res.messages == [ - on_next(210, 1), - on_next(220, 2), - on_next(230, 3), - on_next(240, 4), - on_completed(250)] + assert res.messages == [on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250)] assert subject.disposed def test_ref_count_notconnected(self): @@ -129,6 +123,7 @@ def factory(scheduler): def create(obs, scheduler=None): def func(): disconnected[0] = True + return func return rx.create(create) @@ -174,7 +169,7 @@ def test_publish_basic(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_completed(600) + on_completed(600), ) ys = [None] subscription = [None] @@ -183,38 +178,47 @@ def test_publish_basic(self): def action0(scheduler, state): ys[0] = xs.pipe(ops.publish()) + scheduler.schedule_absolute(created, action0) def action1(scheduler, state): subscription[0] = ys[0].subscribe(results) + scheduler.schedule_absolute(subscribed, action1) def action2(scheduler, state): subscription[0].dispose() + scheduler.schedule_absolute(disposed, action2) def action3(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(300, action3) def action4(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(400, action4) def action5(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(500, action5) def action6(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(550, action6) def action7(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(650, action7) def action8(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(800, action8) scheduler.start() @@ -223,11 +227,12 @@ def action8(scheduler, state): on_next(360, 5), on_next(370, 6), on_next(390, 7), - on_next(520, 11)] + on_next(520, 11), + ] assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 550), subscribe(650, 800)] def test_publish_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable( on_next(110, 7), @@ -243,7 +248,8 @@ def test_publish_error(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_error(600, ex)) + on_error(600, ex), + ) ys = [None] subscription = [None] @@ -252,30 +258,37 @@ def test_publish_error(self): def action0(scheduler, state): ys[0] = xs.pipe(ops.publish()) + scheduler.schedule_absolute(created, action0) def action1(scheduler, state): subscription[0] = ys[0].subscribe(results) + scheduler.schedule_absolute(subscribed, action1) def action2(scheduler, state): subscription[0].dispose() + scheduler.schedule_absolute(disposed, action2) def action3(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(300, action3) def action4(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(400, action4) def action5(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(500, action5) def action6(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(800, action6) scheduler.start() @@ -286,7 +299,8 @@ def action6(scheduler, state): on_next(390, 7), on_next(520, 11), on_next(560, 20), - on_error(600, ex)] + on_error(600, ex), + ] assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 600)] def test_publish_complete(self): @@ -309,35 +323,43 @@ def test_publish_complete(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_completed(600)) + on_completed(600), + ) results = scheduler.create_observer() def action0(scheduler, state): ys[0] = xs.pipe(ops.publish()) + scheduler.schedule_absolute(created, action0) def action1(scheduler, state): subscription[0] = ys[0].subscribe(results) + scheduler.schedule_absolute(subscribed, action1) def action2(scheduler, state): subscription[0].dispose() + scheduler.schedule_absolute(disposed, action2) def action3(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(300, action3) def action4(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(400, action4) def action5(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(500, action5) def action6(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(800, action6) scheduler.start() @@ -348,7 +370,8 @@ def action6(scheduler, state): on_next(390, 7), on_next(520, 11), on_next(560, 20), - on_completed(600)] + on_completed(600), + ] assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 600)] def test_publish_dispose(self): @@ -370,63 +393,70 @@ def test_publish_dispose(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_completed(600)) + on_completed(600), + ) results = scheduler.create_observer() def action0(scheduler, state): ys[0] = xs.pipe(ops.publish()) + scheduler.schedule_absolute(created, action0) def action1(scheduler, state): subscription[0] = ys[0].subscribe(results) + scheduler.schedule_absolute(subscribed, action1) def action2(scheduler, state): subscription[0].dispose() + scheduler.schedule_absolute(350, action2) def action3(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(300, action3) def action4(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(400, action4) def action5(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(500, action5) def action6(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(550, action6) def action7(scheduler, state): connection[0] = ys[0].connect() + scheduler.schedule_absolute(650, action7) def action8(scheduler, state): connection[0].dispose() + scheduler.schedule_absolute(800, action8) scheduler.start() assert results.messages == [on_next(340, 8)] - assert xs.subscriptions == [ - subscribe(300, 400), - subscribe(500, 550), - subscribe(650, 800)] + assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 550), subscribe(650, 800)] def test_publish_multipleconnections(self): xs = rx.never() ys = xs.pipe(ops.publish()) connection1 = ys.connect() connection2 = ys.connect() - assert(connection1 == connection2) + assert connection1 == connection2 connection1.dispose() connection2.dispose() connection3 = ys.connect() - assert(connection1 != connection3) + assert connection1 != connection3 connection3.dispose() def test_publish_lambda_zip_complete(self): @@ -445,16 +475,18 @@ def test_publish_lambda_zip_complete(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_completed(600)) + on_completed(600), + ) def create(): def mapper(_xs): return _xs.pipe( - ops.zip(_xs.pipe(ops.skip(1))), - ops.map(sum), - ) + ops.zip(_xs.pipe(ops.skip(1))), + ops.map(sum), + ) return xs.pipe(ops.publish(mapper)) + results = scheduler.start(create) assert results.messages == [ @@ -469,11 +501,12 @@ def mapper(_xs): on_next(450, 11), on_next(520, 20), on_next(560, 31), - on_completed(600)] + on_completed(600), + ] assert xs.subscriptions == [subscribe(200, 600)] def test_publish_lambda_zip_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable( on_next(110, 7), @@ -489,16 +522,18 @@ def test_publish_lambda_zip_error(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_error(600, ex)) + on_error(600, ex), + ) def create(): def mapper(_xs): return _xs.pipe( - ops.zip(_xs.pipe(ops.skip(1))), - ops.map(sum), - ) + ops.zip(_xs.pipe(ops.skip(1))), + ops.map(sum), + ) return xs.pipe(ops.publish(mapper)) + results = scheduler.start(create) assert results.messages == [ @@ -513,7 +548,8 @@ def mapper(_xs): on_next(450, 11), on_next(520, 20), on_next(560, 31), - on_error(600, ex)] + on_error(600, ex), + ] assert xs.subscriptions == [subscribe(200, 600)] def test_publish_lambda_zip_dispose(self): @@ -532,14 +568,12 @@ def test_publish_lambda_zip_dispose(self): on_next(450, 9), on_next(520, 11), on_next(560, 20), - on_completed(600)) + on_completed(600), + ) def create(): def mapper(_xs): - return _xs.pipe( - ops.zip(_xs.pipe(ops.skip(1))), - ops.map(sum) - ) + return _xs.pipe(ops.zip(_xs.pipe(ops.skip(1))), ops.map(sum)) return xs.pipe(ops.publish(mapper)) @@ -553,5 +587,6 @@ def mapper(_xs): on_next(390, 13), on_next(410, 20), on_next(430, 15), - on_next(450, 11)] + on_next(450, 11), + ] assert xs.subscriptions == [subscribe(200, 470)] diff --git a/tests/test_observable_multiple.py b/tests/test_observable_multiple.py index 886a73e2c..72e1fc349 100644 --- a/tests/test_observable_multiple.py +++ b/tests/test_observable_multiple.py @@ -14,11 +14,10 @@ class RxException(Exception): # Helper function for raising exceptions within lambdas -def _raise(ex): +def _raise(ex: Exception): raise RxException(ex) - # test("Rx.Observable.catchException() does not lose subscription to underlying observable", 12, function () { # var subscribes = 0, # unsubscribes = 0, From 7c985988e7fc0e4711f73426d48eee9a723baceb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 2 Feb 2022 20:06:29 +0100 Subject: [PATCH 002/103] Remove use of Protocol for ABC --- rx/__init__.py | 29 ++++++++++------------------- rx/core/abc/disposable.py | 7 +++---- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 9f97476a0..4827dc5d2 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1,20 +1,12 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin -from typing import (TYPE_CHECKING, Any, Callable, Iterable, Mapping, Optional, - Tuple, TypeVar, Union) +from typing import Any, Callable, Iterable, Mapping, Optional, Tuple, TypeVar, Union from .core import Observable, abc, pipe, typing from .internal.utils import alias from .subject import Subject - -if TYPE_CHECKING: - # Futures cannot take generic argument before Python 3.9 - class _Future: - pass - -else: - from asyncio.futures import Future as _Future +from .core.typing import Future _T = TypeVar("_T") _T1 = TypeVar("_T1") @@ -54,7 +46,7 @@ def amb(*sources: Observable[_T]) -> Observable[_T]: def case( mapper: Callable[[], _T1], sources: Mapping[_T1, _T2], - default_source: Optional[Union[Observable[_T2], _Future]] = None, + default_source: Optional[Union[Observable[_T2], Future]] = None, ) -> Observable[_T2]: """Uses mapper to determine which source in sources to use. @@ -253,7 +245,7 @@ def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: return _concat_with_iterable(sources) -def defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], _Future]]) -> Observable[_T]: +def defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], Future]]) -> Observable[_T]: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -421,7 +413,7 @@ def from_callback(func: Callable, mapper: Optional[typing.Mapper] = None) -> Cal return _from_callback(func, mapper) -def from_future(future: Union[_Future, Observable[_T]]) -> Observable[_T]: +def from_future(future: Union[Future, Observable[_T]]) -> Observable[_T]: """Converts a Future to an Observable sequence .. marble:: @@ -581,8 +573,7 @@ def generate_with_relative_time( Returns: The generated sequence. """ - from .core.observable.generatewithrelativetime import \ - _generate_with_relative_time + from .core.observable.generatewithrelativetime import _generate_with_relative_time return _generate_with_relative_time(initial_state, condition, iterate, time_mapper) @@ -690,8 +681,8 @@ def hot( def if_then( condition: Callable[[], bool], - then_source: Union[Observable, _Future], - else_source: Union[None, Observable, _Future] = None, + then_source: Union[Observable, Future], + else_source: Union[None, Observable, Future] = None, ) -> Observable: """Determines whether an observable collection contains values. @@ -825,7 +816,7 @@ def of(*args: Any) -> Observable: return from_iterable(args) -def on_error_resume_next(*sources: Union[Observable, _Future]) -> Observable: +def on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: """Continues an observable sequence that is terminated normally or by an exception with the next observable sequence. @@ -980,7 +971,7 @@ def start(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Obse return _start(func, scheduler) -def start_async(function_async: Callable[[], _Future]) -> Observable: +def start_async(function_async: Callable[[], Future]) -> Observable: """Invokes the asynchronous function, surfacing the result through an observable sequence. diff --git a/rx/core/abc/disposable.py b/rx/core/abc/disposable.py index abc7be8f1..c41d3ab83 100644 --- a/rx/core/abc/disposable.py +++ b/rx/core/abc/disposable.py @@ -1,10 +1,9 @@ -from abc import abstractmethod +from abc import abstractmethod, ABC from types import TracebackType -from typing import Optional, Protocol, Type, runtime_checkable +from typing import Optional, Type -@runtime_checkable -class DisposableBase(Protocol): +class DisposableBase(ABC): """Disposable abstract base class. Untyped.""" __slots__ = () From 3fe1c3f776d0e3e486ed39fde599cb7eb4695615 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 2 Feb 2022 21:57:49 +0100 Subject: [PATCH 003/103] Remove support for Python 3.6 - Add testing for Python 3.10 instead --- .github/workflows/pythonpackage.yml | 41 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 79335f847..8a9c1f30e 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,29 +6,29 @@ jobs: build: strategy: matrix: - platform: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.6, 3.7, 3.8, 3.9, pypy-3.7] + platform: [ubuntu-latest, macos-latest, windows-latest] + python-version: [3.7, 3.8, 3.9, "3.10", pypy-3.8] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip install -r requirements.txt - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - pytest - + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install -r requirements.txt + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest + coverage: runs-on: ubuntu-latest steps: @@ -49,4 +49,3 @@ jobs: COVERALLS_SERVICE_NAME: github run: | coveralls - \ No newline at end of file From cfa80e60a2f5122ace3e98c52eef675328791ec6 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 2 Feb 2022 23:04:41 +0100 Subject: [PATCH 004/103] Typing fixes ... --- rx/__init__.py | 7 ++--- rx/core/observable/case.py | 24 +++++++++------- rx/core/observable/defer.py | 17 +++++------ rx/core/observable/ifthen.py | 27 +++++++++++------- rx/core/observable/merge.py | 9 ++++-- rx/core/observable/onerrorresumenext.py | 23 ++++++++------- rx/core/operators/amb.py | 35 +++++++++++++---------- rx/core/operators/merge.py | 38 +++++++++++++++---------- rx/core/operators/takeuntil.py | 30 +++++++++++-------- rx/internal/concurrency.py | 11 ++++--- rx/internal/utils.py | 8 +++--- rx/operators/__init__.py | 2 +- 12 files changed, 134 insertions(+), 97 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 4827dc5d2..d119e7277 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -413,7 +413,7 @@ def from_callback(func: Callable, mapper: Optional[typing.Mapper] = None) -> Cal return _from_callback(func, mapper) -def from_future(future: Union[Future, Observable[_T]]) -> Observable[_T]: +def from_future(future: Union[typing.Future, Observable[_T]]) -> Observable[_T]: """Converts a Future to an Observable sequence .. marble:: @@ -425,7 +425,6 @@ def from_future(future: Union[Future, Observable[_T]]) -> Observable[_T]: Args: future: A Python 3 compatible future. https://docs.python.org/3/library/asyncio-task.html#future - http://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.Future Returns: An observable sequence which wraps the existing future success @@ -745,7 +744,7 @@ def interval(period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] return _interval(period, scheduler) -def merge(*sources: Observable) -> Observable: +def merge(*sources: Observable[_T]) -> Observable[_T]: """Merges all the observable sequences into a single observable sequence. .. marble:: @@ -771,7 +770,7 @@ def merge(*sources: Observable) -> Observable: return _merge(*sources) -def never() -> Observable: +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). diff --git a/rx/core/observable/case.py b/rx/core/observable/case.py index 1a4aa7907..b5ca1e69d 100644 --- a/rx/core/observable/case.py +++ b/rx/core/observable/case.py @@ -1,9 +1,7 @@ -from asyncio import Future -from typing import Any, Callable, Mapping, Optional, TypeVar, Union +from typing import Callable, Mapping, Optional, TypeVar, Union from rx import defer, empty, from_future -from rx.core import Observable -from rx.internal.utils import is_future +from rx.core import Observable, typing, abc _Key = TypeVar("_Key") _T = TypeVar("_T") @@ -12,20 +10,24 @@ def _case( mapper: Callable[[], _Key], sources: Mapping[_Key, Observable[_T]], - default_source: Optional[Union[Observable[_T], Future]] = None, + default_source: Optional[Union[Observable[_T], typing.Future]] = None, ) -> Observable[_T]: - default_source: Union[Observable[_T], Future] = default_source or empty() + default_source_: Union[Observable[_T], typing.Future] = default_source or empty() - def factory(_) -> Observable[_T]: + def factory(_: abc.SchedulerBase) -> Observable[_T]: try: - result = sources[mapper()] + result: Union[Observable[_T], typing.Future] = sources[mapper()] except KeyError: - result = default_source + result = default_source_ - result: Observable[_T] = from_future(result) if is_future(result) else result + if isinstance(result, typing.Future): - return result + result_: Observable[_T] = from_future(result) + else: + result_ = result + + return result_ return defer(factory) diff --git a/rx/core/observable/defer.py b/rx/core/observable/defer.py index 4cf4206f0..523b29e29 100644 --- a/rx/core/observable/defer.py +++ b/rx/core/observable/defer.py @@ -1,12 +1,13 @@ -from asyncio import Future -from typing import Callable, Union +from typing import Callable, Optional, Union, TypeVar from rx import from_future, throw -from rx.core import Observable, abc -from rx.internal.utils import is_future +from rx.core import Observable, abc, typing +from rx.scheduler import ImmediateScheduler +_T = TypeVar("_T") -def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable, Future]]) -> Observable: + +def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], typing.Future]]) -> Observable[_T]: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -22,13 +23,13 @@ def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable, Future]]) -> of the given observable factory function. """ - def subscribe(observer, scheduler=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: try: - result = factory(scheduler) + 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 is_future(result) else result + result = from_future(result) if isinstance(result, typing.Future) else result return result.subscribe(observer, scheduler=scheduler) return Observable(subscribe) diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index 10e183a3c..6e0b92c3a 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -1,15 +1,16 @@ -from asyncio import Future -from typing import Callable, Union, cast +from typing import Callable, Union, cast, TypeVar import rx -from rx.core import Observable, abc -from rx.internal.utils import is_future +from rx.core import Observable, abc, typing +_T = TypeVar("_T") -def _if_then(condition: Callable[[], bool], - then_source: Union[Observable, Future], - else_source: Union[None, Observable, Future] = None - ) -> Observable: + +def _if_then( + condition: Callable[[], bool], + then_source: Union[Observable[_T], typing.Future], + else_source: Union[None, Observable[_T], typing.Future] = None, +) -> Observable[_T]: """Determines whether an observable collection contains values. Example: @@ -33,10 +34,14 @@ def _if_then(condition: Callable[[], bool], else_source = else_source or rx.empty() - then_source = rx.from_future(cast(Future, then_source)) if is_future(then_source) else then_source - else_source = rx.from_future(cast(Future, else_source)) if is_future(else_source) else else_source + then_source = ( + rx.from_future(cast(typing.Future, then_source)) if isinstance(then_source, typing.Future) else then_source + ) + else_source = ( + rx.from_future(cast(typing.Future, else_source)) if isinstance(else_source, typing.Future) else else_source + ) - def factory(_: abc.SchedulerBase): + def factory(_: abc.SchedulerBase) -> Observable[_T]: return then_source if condition() else else_source return rx.defer(factory) diff --git a/rx/core/observable/merge.py b/rx/core/observable/merge.py index 4fc004fd9..62c39aac6 100644 --- a/rx/core/observable/merge.py +++ b/rx/core/observable/merge.py @@ -1,8 +1,13 @@ - +from typing import TypeVar import rx from rx import operators as ops from rx.core import Observable +_T = TypeVar("_T") + -def _merge(*sources: Observable) -> Observable: +def _merge(*sources: Observable[_T]) -> Observable[_T]: return rx.from_iterable(sources).pipe(ops.merge_all()) + + +__all__ = ["_merge"] diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index ff0db84f6..23a228830 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,15 +1,14 @@ -from asyncio import Future -from typing import Union +from typing import Optional, Union, TypeVar, Any import rx -from rx.core import Observable -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) -from rx.internal.utils import is_future +from rx.core import Observable, typing, abc +from rx.disposable import CompositeDisposable, SerialDisposable, SingleAssignmentDisposable from rx.scheduler import CurrentThreadScheduler +_T = TypeVar("_T") -def _on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: + +def _on_error_resume_next(*sources: Union[Observable[_T], typing.Future]) -> Observable[_T]: """Continues an observable sequence that is terminated normally or by an exception with the next observable sequence. @@ -23,14 +22,14 @@ def _on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: sources_ = iter(sources) - def subscribe(observer, scheduler=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): scheduler = scheduler or CurrentThreadScheduler.singleton() subscription = SerialDisposable() cancelable = SerialDisposable() - def action(scheduler, state=None): + def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): try: source = next(sources_) except StopIteration: @@ -39,7 +38,7 @@ def action(scheduler, state=None): # Allow source to be a factory method taking an error source = source(state) if callable(source) else source - current = rx.from_future(source) if is_future(source) else source + current = rx.from_future(source) if isinstance(source, typing.Future) else source d = SingleAssignmentDisposable() subscription.disposable = d @@ -51,4 +50,8 @@ def on_resume(state=None): cancelable.disposable = scheduler.schedule(action) return CompositeDisposable(subscription, cancelable) + return Observable(subscribe) + + +__all__ = ["_on_error_resume_next"] diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index decf2e692..1b447151a 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -1,22 +1,24 @@ -from asyncio import Future -from typing import Any, Union, cast +from typing import Callable, List, Optional, Union, cast, TypeVar from rx import from_future -from rx.core import Observable, abc +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -from rx.internal.utils import is_future +_T = TypeVar("_T") -def _amb(right_source: Observable): - if is_future(right_source): - obs = from_future(cast(Future, right_source)) +def _amb(right_source: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: + + if isinstance(right_source, typing.Future): + obs: Observable[_T] = cast(Observable[_T], from_future(right_source)) else: - obs = cast(Observable, right_source) + obs: Observable[_T] = right_source - def amb(left_source: Observable): - def subscribe(observer: abc.ObserverBase, scheduler: abc.SchedulerBase = None) -> abc.DisposableBase: - choice = [None] + def amb(left_source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + choice: List[Optional[str]] = [None] left_choice = "L" right_choice = "R" left_subscription = SingleAssignmentDisposable() @@ -32,19 +34,19 @@ def choice_right(): choice[0] = right_choice left_subscription.dispose() - def on_next_left(value): + def on_next_left(value: _T) -> None: with left_source.lock: choice_left() if choice[0] == left_choice: observer.on_next(value) - def on_error_left(err): + def on_error_left(err: Exception) -> None: with left_source.lock: choice_left() if choice[0] == left_choice: observer.on_error(err) - def on_completed_left(): + def on_completed_left() -> None: with left_source.lock: choice_left() if choice[0] == left_choice: @@ -53,7 +55,7 @@ def on_completed_left(): left_d = left_source.subscribe_(on_next_left, on_error_left, on_completed_left, scheduler) left_subscription.disposable = left_d - def send_right(value: Any) -> None: + def send_right(value: _T) -> None: with left_source.lock: choice_right() if choice[0] == right_choice: @@ -78,3 +80,6 @@ def on_completed_right() -> None: return Observable(subscribe) return amb + + +__all__ = ["_amb"] diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index c6a2c487c..cb7c82dd7 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -1,18 +1,19 @@ -from typing import Callable, Optional +from typing import Callable, List, Optional, TypeVar import rx from rx import from_future -from rx.core import Observable +from rx.core import Observable, typing, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.concurrency import synchronized from rx.internal.utils import is_future +_T = TypeVar("_T") -def _merge(*sources: Observable, - max_concurrent: Optional[int] = None - ) -> Callable[[Observable], Observable]: - def merge(source: Observable) -> Observable: +def _merge( + *sources: Observable[_T], max_concurrent: Optional[int] = None +) -> Callable[[Observable[Observable[_T]]], Observable[_T]]: + def merge(source: Observable[Observable[_T]]) -> Observable[_T]: """Merges an observable sequence of observable sequences into an observable sequence, limiting the number of concurrent subscriptions to inner sequences. Or merges two observable @@ -33,13 +34,13 @@ def merge(source: Observable) -> Observable: sources_ = tuple([source]) + sources return rx.merge(*sources_) - def subscribe(observer, scheduler=None): + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): active_count = [0] group = CompositeDisposable() is_stopped = [False] - queue = [] + queue: List[Observable[_T]] = [] - def subscribe(xs): + def subscribe(xs: Observable[_T]): subscription = SingleAssignmentDisposable() group.add(subscription) @@ -58,7 +59,7 @@ def on_completed(): on_error = synchronized(source.lock)(observer.on_error) subscription.disposable = xs.subscribe_(on_next, on_error, on_completed, scheduler) - def on_next(inner_source): + def on_next(inner_source: Observable[_T]) -> None: if active_count[0] < max_concurrent: active_count[0] += 1 subscribe(inner_source) @@ -72,12 +73,14 @@ def on_completed(): group.add(source.subscribe_(on_next, observer.on_error, on_completed, scheduler)) return group + return Observable(subscribe) + return merge -def _merge_all() -> Callable[[Observable], Observable]: - def merge_all(source: Observable) -> Observable: +def _merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: + def merge_all(source: Observable[Observable[_T]]) -> Observable[_T]: """Partially applied merge_all operator. Merges an observable sequence of observable sequences into an @@ -90,13 +93,14 @@ def merge_all(source: Observable) -> Observable: The observable sequence that merges the elements of the inner sequences. """ - def subscribe(observer, scheduler=None): + + def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): group = CompositeDisposable() is_stopped = [False] m = SingleAssignmentDisposable() group.add(m) - def on_next(inner_source): + def on_next(inner_source: Observable[_T]): inner_subscription = SingleAssignmentDisposable() group.add(inner_subscription) @@ -108,7 +112,7 @@ def on_completed(): if is_stopped[0] and len(group) == 1: observer.on_completed() - on_next = synchronized(source.lock)(observer.on_next) + on_next: typing.OnNext[_T] = synchronized(source.lock)(observer.on_next) on_error = synchronized(source.lock)(observer.on_error) subscription = inner_source.subscribe_(on_next, on_error, on_completed, scheduler) inner_subscription.disposable = subscription @@ -122,4 +126,8 @@ def on_completed(): return group return Observable(subscribe) + return merge_all + + +__all__ = ["_merge", "_merge_all"] diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index e63080a39..91032f971 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -1,20 +1,21 @@ -from asyncio import Future -from typing import Callable, Union, cast +from typing import Callable, Optional, Union, cast, TypeVar from rx import from_future -from rx.core import Observable +from rx.core import Observable, typing, abc from rx.disposable import CompositeDisposable from rx.internal import noop from rx.internal.utils import is_future +_T = TypeVar("_T") -def _take_until(other: Union[Observable, Future]) -> Callable[[Observable], Observable]: + +def _take_until(other: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: if is_future(other): - obs = from_future(cast(Future, other)) + obs: Observable[_T] = from_future(other) else: - obs = cast(Observable, other) + obs = cast(Observable[_T], other) - def take_until(source: Observable) -> Observable: + def take_until(source: Observable[_T]) -> Observable[_T]: """Returns the values from the source observable sequence until the other observable sequence produces a value. @@ -27,14 +28,19 @@ def take_until(source: Observable) -> Observable: further propagation. """ - def subscribe(observer, scheduler=None): - - def on_completed(_): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + def on_completed(_: _T) -> None: observer.on_completed() return CompositeDisposable( - source.subscribe(observer), - obs.subscribe_(on_completed, observer.on_error, noop, scheduler) + source.subscribe(observer), obs.subscribe_(on_completed, observer.on_error, noop, scheduler) ) + return Observable(subscribe) + return take_until + + +__all__ = ["_take_until"] diff --git a/rx/internal/concurrency.py b/rx/internal/concurrency.py index 57665b2ea..6af9021d6 100644 --- a/rx/internal/concurrency.py +++ b/rx/internal/concurrency.py @@ -1,4 +1,5 @@ -from threading import Thread +from threading import Thread, RLock +from typing import Callable, Any from rx.core.typing import StartableTarget @@ -7,12 +8,14 @@ def default_thread_factory(target: StartableTarget) -> Thread: return Thread(target=target, daemon=True) -def synchronized(lock): +def synchronized(lock: RLock): """A decorator for synchronizing access to a given function.""" - def wrapper(fn): - def inner(*args, **kw): + def wrapper(fn: Callable[..., Any]) -> Callable[..., Any]: + def inner(*args: Any, **kw: Any) -> Any: with lock: return fn(*args, **kw) + return inner + return wrapper diff --git a/rx/internal/utils.py b/rx/internal/utils.py index 39e5df21b..95fbfca45 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -2,21 +2,21 @@ from types import FunctionType from typing import Any, Callable, Iterable, Optional, cast -from rx.core.abc import ObservableBase, ObserverBase, SchedulerBase +from rx.core import abc, typing from rx.disposable import CompositeDisposable -def add_ref(xs: ObservableBase[Any], r): +def add_ref(xs: abc.ObservableBase[Any], r): from rx.core import Observable - def subscribe(observer: ObserverBase[Any], scheduler: Optional[SchedulerBase] = None): + def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None): return CompositeDisposable(r.disposable, xs.subscribe(observer)) return Observable(subscribe) def is_future(fut: Any) -> bool: - return callable(getattr(fut, "add_done_callback", None)) + return isinstance(fut, typing.Future) def infinite() -> Iterable[int]: diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 306bd576f..90ae45835 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1697,7 +1697,7 @@ def merge(*sources: Observable, max_concurrent: Optional[int] = None) -> Callabl return _merge(*sources, max_concurrent=max_concurrent) -def merge_all() -> Callable[[Observable], Observable]: +def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: """The merge_all operator. Merges an observable sequence of observable sequences into an From d5aa10029e72cf057587044565ccdc2e929e2955 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 3 Feb 2022 08:40:47 +0100 Subject: [PATCH 005/103] Fixed generate + cleanup --- .gitignore | 2 + examples/autocomplete/autocomplete.py | 26 +++---- rx/__init__.py | 74 ++++++++++++++----- rx/core/__init__.py | 9 ++- .../observable/generatewithrelativetime.py | 41 ++++++---- rx/scheduler/__init__.py | 14 ++++ rx/scheduler/timeoutscheduler.py | 20 +++-- rx/scheduler/trampoline.py | 3 + rx/subject/innersubscription.py | 8 +- setup.cfg | 3 + 10 files changed, 143 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 6229c880a..cef3c419a 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ _build # Type checkers .pyre + +.ionide/ diff --git a/examples/autocomplete/autocomplete.py b/examples/autocomplete/autocomplete.py index 4b50c2a94..c8dec5cde 100644 --- a/examples/autocomplete/autocomplete.py +++ b/examples/autocomplete/autocomplete.py @@ -24,20 +24,16 @@ def search_wikipedia(term): """Search Wikipedia for a given term""" - url = 'http://en.wikipedia.org/w/api.php' + url = "http://en.wikipedia.org/w/api.php" - params = { - "action": 'opensearch', - "search": term, - "format": 'json' - } + params = {"action": "opensearch", "search": term, "format": "json"} # Must set a user agent for non-browser requests to Wikipedia user_agent = "RxPY/3.0 (https://github.com/dbrattli/RxPY; dag@brattli.net) Tornado/4.0.1" url = url_concat(url, params) http_client = AsyncHTTPClient() - return http_client.fetch(url, method='GET', user_agent=user_agent) + return http_client.fetch(url, method="GET", user_agent=user_agent) class WSHandler(WebSocketHandler): @@ -52,9 +48,9 @@ def open(self): searcher = self.stream.pipe( ops.map(lambda x: x["term"]), ops.filter(lambda text: len(text) > 2), # Only if the text is longer than 2 characters - ops.debounce(0.750), # Pause for 750ms - ops.distinct_until_changed(), # Only if the value has changed - ops.flat_map_latest(search_wikipedia) + ops.debounce(0.750), # Pause for 750ms + ops.distinct_until_changed(), # Only if the value has changed + ops.flat_map_latest(search_wikipedia), ) def send_response(x): @@ -80,15 +76,13 @@ def get(self): def main(): port = os.environ.get("PORT", 8080) - app = Application([ - url(r"/", MainHandler), - (r'/ws', WSHandler), - (r'/static/(.*)', StaticFileHandler, {'path': "."}) - ]) + app = Application( + [url(r"/", MainHandler), (r"/ws", WSHandler), (r"/static/(.*)", StaticFileHandler, {"path": "."})] + ) print("Starting server at port: %s" % port) app.listen(port) ioloop.IOLoop.current().start() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/rx/__init__.py b/rx/__init__.py index d119e7277..70195a230 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -11,6 +11,8 @@ _T = TypeVar("_T") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") +_TState = TypeVar("_TState") + # Please make sure the version here remains the same as in project.cfg __version__ = "3.2.0" @@ -245,7 +247,9 @@ def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: return _concat_with_iterable(sources) -def defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], Future]]) -> Observable[_T]: +def defer( + factory: Callable[[abc.SchedulerBase], Union[Observable[_T], Future]] +) -> Observable[_T]: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -362,7 +366,9 @@ def fork_join(*sources: Observable[_T]) -> Observable[_T]: return _fork_join(*sources) -def from_callable(supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: +def from_callable( + supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[_T]: """Returns an observable sequence that contains a single element generated by the given supplier, using the specified scheduler to send out observer messages. @@ -393,7 +399,9 @@ def from_callable(supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerB return _from_callable(supplier, scheduler) -def from_callback(func: Callable, mapper: Optional[typing.Mapper] = None) -> Callable[[], Observable]: +def from_callback( + func: Callable, mapper: Optional[typing.Mapper] = None +) -> Callable[[], Observable]: """Converts a callback function to an observable sequence. Args: @@ -435,7 +443,9 @@ def from_future(future: Union[typing.Future, Observable[_T]]) -> Observable[_T]: return _from_future(future) -def from_iterable(iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: +def from_iterable( + iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[_T]: """Converts an iterable to an observable sequence. .. marble:: @@ -535,18 +545,20 @@ def from_marbles( from .core.observable.marbles import from_marbles as _from_marbles - return _from_marbles(string, timespan, lookup=lookup, error=error, scheduler=scheduler) + return _from_marbles( + string, timespan, lookup=lookup, error=error, scheduler=scheduler + ) cold = alias("cold", "Alias for :func:`rx.from_marbles`.", from_marbles) def generate_with_relative_time( - initial_state: Any, - condition: typing.Predicate, - iterate: typing.Mapper, - time_mapper: Callable[[Any], typing.RelativeTime], -) -> Observable: + initial_state: _TState, + condition: typing.Predicate[_TState], + iterate: typing.Mapper[_TState, _TState], + time_mapper: Callable[[_TState], typing.RelativeTime], +) -> Observable[_TState]: """Generates an observable sequence by iterating a state from an initial state until the condition fails. @@ -577,7 +589,9 @@ def generate_with_relative_time( return _generate_with_relative_time(initial_state, condition, iterate, time_mapper) -def generate(initial_state: Any, condition: typing.Predicate, iterate: typing.Mapper) -> Observable: +def generate( + initial_state: Any, condition: typing.Predicate, iterate: typing.Mapper +) -> Observable: """Generates an observable sequence by running a state-driven loop producing the sequence's elements. @@ -675,7 +689,9 @@ def hot( from .core.observable.marbles import hot as _hot - return _hot(string, timespan, duetime, lookup=lookup, error=error, scheduler=scheduler) + return _hot( + string, timespan, duetime, lookup=lookup, error=error, scheduler=scheduler + ) def if_then( @@ -716,7 +732,9 @@ def if_then( return _if_then(condition, then_source, else_source) -def interval(period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: +def interval( + period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable: """Returns an observable sequence that produces a value after each period. .. marble:: @@ -845,7 +863,10 @@ def on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: def range( - start: int, stop: Optional[int] = None, step: Optional[int] = None, scheduler: Optional[abc.SchedulerBase] = None + start: int, + stop: Optional[int] = None, + step: Optional[int] = None, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable: """Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to send out observer @@ -878,7 +899,9 @@ def range( return _range(start, stop, step, scheduler) -def return_value(value: Any, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: +def return_value( + value: Any, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable: """Returns an observable sequence that contains a single element, using the specified scheduler to send out observer messages. There is an alias called 'just'. @@ -993,7 +1016,9 @@ def start_async(function_async: Callable[[], Future]) -> Observable: return _start_async(function_async) -def throw(exception: Exception, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: +def throw( + exception: Exception, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable: """Returns an observable sequence that terminates with an exception, using the specified scheduler to send out the single OnError message. @@ -1061,7 +1086,9 @@ def timer( return _timer(duetime, period, scheduler) -def to_async(func: Callable[..., Any], scheduler: Optional[abc.SchedulerBase] = None) -> Callable: +def to_async( + func: Callable[..., Any], scheduler: Optional[abc.SchedulerBase] = None +) -> Callable: """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 @@ -1175,4 +1202,15 @@ def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: return _zip(*args) -__all__ = ["never", "pipe", "with_latest_from", "throw", "timer", "using", "to_async", "zip", "return_value", "Subject"] +__all__ = [ + "never", + "pipe", + "with_latest_from", + "throw", + "timer", + "using", + "to_async", + "zip", + "return_value", + "Subject", +] diff --git a/rx/core/__init__.py b/rx/core/__init__.py index e33c89f51..e2dfc1dff 100644 --- a/rx/core/__init__.py +++ b/rx/core/__init__.py @@ -3,4 +3,11 @@ from .observer import Observer from .pipe import pipe -__all__ = ["abc", "pipe", "Observable", "ConnectableObservable", "GroupedObservable", "Observer"] +__all__ = [ + "abc", + "pipe", + "Observable", + "ConnectableObservable", + "GroupedObservable", + "Observer", +] diff --git a/rx/core/observable/generatewithrelativetime.py b/rx/core/observable/generatewithrelativetime.py index a0d676b51..d6754fa36 100644 --- a/rx/core/observable/generatewithrelativetime.py +++ b/rx/core/observable/generatewithrelativetime.py @@ -1,21 +1,25 @@ -from typing import Any, Callable +from typing import Any, Callable, Optional, TypeVar, cast -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Mapper, Predicate, RelativeTime from rx.disposable import MultipleAssignmentDisposable from rx.scheduler import TimeoutScheduler +_TState = TypeVar("_TState") -def _generate_with_relative_time(initial_state: Any, - condition: Predicate, - iterate: Mapper, - time_mapper: Callable[[Any], RelativeTime] - ) -> Observable: + +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) + res = source.generate_with_relative_time(0, lambda x: True, + lambda x: x + 1, lambda x: 0.5) Args: initial_state: Initial state. @@ -23,23 +27,27 @@ def _generate_with_relative_time(initial_state: Any, 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. + 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, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_TState], + scheduler: Optional[abc.SchedulerBase] = None, + ): scheduler = scheduler or TimeoutScheduler.singleton() mad = MultipleAssignmentDisposable() state = initial_state has_result = False - result = None + result: _TState = cast(_TState, None) first = True - time = None + time: Optional[RelativeTime] = None - def action(scheduler, _): + def action(scheduler: abc.SchedulerBase, _: Any): nonlocal state nonlocal has_result nonlocal result @@ -66,10 +74,15 @@ def action(scheduler, _): 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"] diff --git a/rx/scheduler/__init__.py b/rx/scheduler/__init__.py index 044b5a5ac..1205e8299 100644 --- a/rx/scheduler/__init__.py +++ b/rx/scheduler/__init__.py @@ -9,3 +9,17 @@ from .timeoutscheduler import TimeoutScheduler from .trampolinescheduler import TrampolineScheduler from .virtualtimescheduler import VirtualTimeScheduler + +__all__ = [ + "CatchScheduler", + "CurrentThreadScheduler", + "EventLoopScheduler", + "HistoricalScheduler", + "ImmediateScheduler", + "NewThreadScheduler", + "ScheduledItem", + "ThreadPoolScheduler", + "TimeoutScheduler", + "TrampolineScheduler", + "VirtualTimeScheduler", +] diff --git a/rx/scheduler/timeoutscheduler.py b/rx/scheduler/timeoutscheduler.py index ec957a78f..72125609a 100644 --- a/rx/scheduler/timeoutscheduler.py +++ b/rx/scheduler/timeoutscheduler.py @@ -3,8 +3,7 @@ from weakref import WeakKeyDictionary from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from .periodicscheduler import PeriodicScheduler @@ -30,7 +29,9 @@ def singleton(cls) -> "TimeoutScheduler": def __new__(cls) -> "TimeoutScheduler": return cls.singleton() - def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -57,7 +58,10 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) def schedule_relative( - self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -90,7 +94,10 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. @@ -106,3 +113,6 @@ def schedule_absolute( duetime = self.to_datetime(duetime) return self.schedule_relative(duetime - self.now, action, state) + + +__all__ = ["TimeoutScheduler"] diff --git a/rx/scheduler/trampoline.py b/rx/scheduler/trampoline.py index c6e585187..80b24c985 100644 --- a/rx/scheduler/trampoline.py +++ b/rx/scheduler/trampoline.py @@ -57,3 +57,6 @@ def _run(self) -> None: seconds = (item.duetime - item.scheduler.now).total_seconds() if seconds > 0.0: self._condition.wait(seconds) + + +__all__ = ["Trampoline"] diff --git a/rx/subject/innersubscription.py b/rx/subject/innersubscription.py index 05d826957..e0e47ebc3 100644 --- a/rx/subject/innersubscription.py +++ b/rx/subject/innersubscription.py @@ -1,14 +1,16 @@ import threading -from typing import TypeVar +from typing import TYPE_CHECKING, TypeVar from rx.core import abc -from rx.core.abc.observable import ObservableBase + +if TYPE_CHECKING: + from .subject import Subject _T = TypeVar("_T") class InnerSubscription(abc.DisposableBase): - def __init__(self, subject: abc.SubjectBase[_T], observer: ObservableBase[_T]): + def __init__(self, subject: "Subject[_T]", observer: abc.ObserverBase[_T]): self.subject = subject self.observer = observer diff --git a/setup.cfg b/setup.cfg index dd76926ad..a323191d8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,6 @@ test = pytest [tool:pytest] testpaths = tests + +[flake8] +max-line-length = 120 \ No newline at end of file From f4d284b98468589334975407cc2816c541b40062 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 08:01:12 +0100 Subject: [PATCH 006/103] Fixes for typed Futures --- .python-version | 1 + examples/autocomplete/autocomplete.py | 25 ++- rx/__init__.py | 5 +- rx/core/observable/case.py | 11 +- rx/core/observable/defer.py | 16 +- rx/core/observable/fromfuture.py | 20 +- rx/core/observable/ifthen.py | 9 +- rx/core/observable/onerrorresumenext.py | 21 +- rx/core/operators/all.py | 15 +- rx/core/operators/amb.py | 18 +- rx/core/operators/asobservable.py | 19 +- rx/core/operators/debounce.py | 50 +++-- rx/core/operators/flatmap.py | 17 +- rx/core/operators/singleordefault.py | 22 +- rx/core/operators/skipuntil.py | 22 +- rx/core/operators/takeuntil.py | 13 +- rx/core/operators/tofuture.py | 26 ++- rx/core/operators/toiterable.py | 32 ++- rx/core/pipe.py | 29 +-- rx/core/typing.py | 31 ++- rx/internal/utils.py | 9 +- rx/operators/__init__.py | 192 +++++++++++++----- .../eventloop/asynciothreadsafescheduler.py | 27 +-- tests/test_observable/test_flatmap_async.py | 16 +- 24 files changed, 424 insertions(+), 222 deletions(-) create mode 100644 .python-version diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..89a1ad7ad --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.8.12 diff --git a/examples/autocomplete/autocomplete.py b/examples/autocomplete/autocomplete.py index c8dec5cde..559494fcb 100644 --- a/examples/autocomplete/autocomplete.py +++ b/examples/autocomplete/autocomplete.py @@ -7,6 +7,7 @@ """ import os +from typing import Any, Dict from tornado.websocket import WebSocketHandler from tornado.web import RequestHandler, StaticFileHandler, Application, url @@ -15,7 +16,7 @@ from tornado.escape import json_decode from tornado import ioloop -from rx import operators as ops +from rx import Observable, operators as ops from rx.subject import Subject from rx.scheduler.eventloop import IOLoopScheduler @@ -26,9 +27,11 @@ def search_wikipedia(term): """Search Wikipedia for a given term""" url = "http://en.wikipedia.org/w/api.php" - params = {"action": "opensearch", "search": term, "format": "json"} + params: Dict[str, str] = {"action": "opensearch", "search": term, "format": "json"} # Must set a user agent for non-browser requests to Wikipedia - user_agent = "RxPY/3.0 (https://github.com/dbrattli/RxPY; dag@brattli.net) Tornado/4.0.1" + user_agent = ( + "RxPY/3.0 (https://github.com/dbrattli/RxPY; dag@brattli.net) Tornado/4.0.1" + ) url = url_concat(url, params) @@ -37,17 +40,19 @@ def search_wikipedia(term): class WSHandler(WebSocketHandler): - def open(self): + def open(self, *args: Any): print("WebSocket opened") # A Subject is both an observable and observer, so we can both subscribe # to it and also feed (send) it with new values - self.stream = Subject() + self.stream: Subject[Dict[str, str]] = Subject() # Get all distinct key up events from the input and only fire if long enough and distinct searcher = self.stream.pipe( ops.map(lambda x: x["term"]), - ops.filter(lambda text: len(text) > 2), # Only if the text is longer than 2 characters + ops.filter( + lambda text: len(text) > 2 + ), # Only if the text is longer than 2 characters ops.debounce(0.750), # Pause for 750ms ops.distinct_until_changed(), # Only if the value has changed ops.flat_map_latest(search_wikipedia), @@ -56,7 +61,7 @@ def open(self): def send_response(x): self.write_message(x.body) - def on_error(ex): + def on_error(ex: Exception): print(ex) searcher.subscribe(send_response, on_error, scheduler=scheduler) @@ -77,7 +82,11 @@ def get(self): def main(): port = os.environ.get("PORT", 8080) app = Application( - [url(r"/", MainHandler), (r"/ws", WSHandler), (r"/static/(.*)", StaticFileHandler, {"path": "."})] + [ + url(r"/", MainHandler), + (r"/ws", WSHandler), + (r"/static/(.*)", StaticFileHandler, {"path": "."}), + ] ) print("Starting server at port: %s" % port) app.listen(port) diff --git a/rx/__init__.py b/rx/__init__.py index 70195a230..b64e5e6d1 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1,12 +1,11 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin - +from asyncio import Future from typing import Any, Callable, Iterable, Mapping, Optional, Tuple, TypeVar, Union from .core import Observable, abc, pipe, typing from .internal.utils import alias from .subject import Subject -from .core.typing import Future _T = TypeVar("_T") _T1 = TypeVar("_T1") @@ -421,7 +420,7 @@ def from_callback( return _from_callback(func, mapper) -def from_future(future: Union[typing.Future, Observable[_T]]) -> Observable[_T]: +def from_future(future: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: """Converts a Future to an Observable sequence .. marble:: diff --git a/rx/core/observable/case.py b/rx/core/observable/case.py index b5ca1e69d..af9cf962c 100644 --- a/rx/core/observable/case.py +++ b/rx/core/observable/case.py @@ -1,7 +1,8 @@ +from asyncio import Future from typing import Callable, Mapping, Optional, TypeVar, Union from rx import defer, empty, from_future -from rx.core import Observable, typing, abc +from rx.core import Observable, abc _Key = TypeVar("_Key") _T = TypeVar("_T") @@ -10,18 +11,18 @@ def _case( mapper: Callable[[], _Key], sources: Mapping[_Key, Observable[_T]], - default_source: Optional[Union[Observable[_T], typing.Future]] = None, + default_source: Optional[Union[Observable[_T], "Future[_T]"]] = None, ) -> Observable[_T]: - default_source_: Union[Observable[_T], typing.Future] = default_source or empty() + default_source_: Union[Observable[_T], "Future[_T]"] = default_source or empty() def factory(_: abc.SchedulerBase) -> Observable[_T]: try: - result: Union[Observable[_T], typing.Future] = sources[mapper()] + result: Union[Observable[_T], "Future[_T]"] = sources[mapper()] except KeyError: result = default_source_ - if isinstance(result, typing.Future): + if isinstance(result, Future): result_: Observable[_T] = from_future(result) else: diff --git a/rx/core/observable/defer.py b/rx/core/observable/defer.py index 523b29e29..ce1aab178 100644 --- a/rx/core/observable/defer.py +++ b/rx/core/observable/defer.py @@ -1,13 +1,16 @@ +from asyncio import Future from typing import Callable, Optional, Union, TypeVar from rx import from_future, throw -from rx.core import Observable, abc, typing +from rx.core import Observable, abc from rx.scheduler import ImmediateScheduler _T = TypeVar("_T") -def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], typing.Future]]) -> Observable[_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. @@ -23,13 +26,18 @@ def _defer(factory: Callable[[abc.SchedulerBase], Union[Observable[_T], typing.F of the given observable factory function. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + 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, typing.Future) else result + result = from_future(result) if isinstance(result, Future) else result return result.subscribe(observer, scheduler=scheduler) return Observable(subscribe) + + +__all__ = ["_defer"] diff --git a/rx/core/observable/fromfuture.py b/rx/core/observable/fromfuture.py index a41c87a2a..58b3e6827 100644 --- a/rx/core/observable/fromfuture.py +++ b/rx/core/observable/fromfuture.py @@ -1,11 +1,14 @@ import asyncio -from typing import Any, Optional +from asyncio import Future +from typing import Any, Optional, TypeVar -from rx.core import Observable, abc, typing +from rx.core import Observable, abc from rx.disposable import Disposable +_T = TypeVar("_T") -def _from_future(future: typing.Future) -> Observable[Any]: + +def _from_future(future: "Future[_T]") -> Observable[_T]: """Converts a Future to an Observable sequence Args: @@ -18,11 +21,16 @@ def _from_future(future: typing.Future) -> Observable[Any]: and failure. """ - def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: - def done(future: typing.Future): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + def done(future: "Future[_T]"): try: value: Any = future.result() - except (Exception, asyncio.CancelledError) as ex: # pylint: disable=broad-except + except ( + Exception, + asyncio.CancelledError, + ) as ex: # pylint: disable=broad-except observer.on_error(ex) else: observer.on_next(value) diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index 6e0b92c3a..58426d2a5 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -1,3 +1,4 @@ +from asyncio import Future from typing import Callable, Union, cast, TypeVar import rx @@ -8,8 +9,8 @@ def _if_then( condition: Callable[[], bool], - then_source: Union[Observable[_T], typing.Future], - else_source: Union[None, Observable[_T], typing.Future] = None, + 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. @@ -35,10 +36,10 @@ def _if_then( else_source = else_source or rx.empty() then_source = ( - rx.from_future(cast(typing.Future, then_source)) if isinstance(then_source, typing.Future) else then_source + rx.from_future(then_source) if isinstance(then_source, Future) else then_source ) else_source = ( - rx.from_future(cast(typing.Future, else_source)) if isinstance(else_source, typing.Future) else else_source + rx.from_future(else_source) if isinstance(else_source, Future) else else_source ) def factory(_: abc.SchedulerBase) -> Observable[_T]: diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 23a228830..73c8bdd4c 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,14 +1,21 @@ +from asyncio import Future from typing import Optional, Union, TypeVar, Any import rx from rx.core import Observable, typing, abc -from rx.disposable import CompositeDisposable, SerialDisposable, SingleAssignmentDisposable +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.scheduler import CurrentThreadScheduler _T = TypeVar("_T") -def _on_error_resume_next(*sources: Union[Observable[_T], typing.Future]) -> Observable[_T]: +def _on_error_resume_next( + *sources: Union[Observable[_T], "Future[_T]"] +) -> Observable[_T]: """Continues an observable sequence that is terminated normally or by an exception with the next observable sequence. @@ -22,7 +29,9 @@ def _on_error_resume_next(*sources: Union[Observable[_T], typing.Future]) -> Obs sources_ = iter(sources) - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ): scheduler = scheduler or CurrentThreadScheduler.singleton() @@ -38,7 +47,7 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): # Allow source to be a factory method taking an error source = source(state) if callable(source) else source - current = rx.from_future(source) if isinstance(source, typing.Future) else source + current = rx.from_future(source) if isinstance(source, Future) else source d = SingleAssignmentDisposable() subscription.disposable = d @@ -46,7 +55,9 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): def on_resume(state=None): scheduler.schedule(action, state) - d.disposable = current.subscribe_(observer.on_next, on_resume, on_resume, scheduler) + d.disposable = current.subscribe_( + observer.on_next, on_resume, on_resume, scheduler + ) cancelable.disposable = scheduler.schedule(action) return CompositeDisposable(subscription, cancelable) diff --git a/rx/core/operators/all.py b/rx/core/operators/all.py index 0bbed4afb..1c5b02e18 100644 --- a/rx/core/operators/all.py +++ b/rx/core/operators/all.py @@ -1,18 +1,19 @@ -from typing import Callable +from typing import Callable, TypeVar from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Predicate +_T = TypeVar("_T") -def _all(predicate: Predicate) -> Callable[[Observable], Observable]: + +def _all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: filtering = ops.filter(lambda v: not predicate(v)) mapping = ops.map(lambda b: not b) some = ops.some() - return pipe( - filtering, - some, - mapping - ) + return pipe(filtering, some, mapping) + + +__all__ = ["_all"] diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 1b447151a..3359e29bc 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -1,3 +1,4 @@ +from asyncio import Future from typing import Callable, List, Optional, Union, cast, TypeVar from rx import from_future @@ -7,16 +8,19 @@ _T = TypeVar("_T") -def _amb(right_source: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: +def _amb( + right_source: Union[Observable[_T], "Future[_T]"] +) -> Callable[[Observable[_T]], Observable[_T]]: - if isinstance(right_source, typing.Future): + if isinstance(right_source, Future): obs: Observable[_T] = cast(Observable[_T], from_future(right_source)) else: obs: Observable[_T] = right_source def amb(left_source: Observable[_T]) -> Observable[_T]: def subscribe( - observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: choice: List[Optional[str]] = [None] left_choice = "L" @@ -52,7 +56,9 @@ def on_completed_left() -> None: if choice[0] == left_choice: observer.on_completed() - left_d = left_source.subscribe_(on_next_left, on_error_left, on_completed_left, scheduler) + left_d = left_source.subscribe_( + on_next_left, on_error_left, on_completed_left, scheduler + ) left_subscription.disposable = left_d def send_right(value: _T) -> None: @@ -73,7 +79,9 @@ def on_completed_right() -> None: if choice[0] == right_choice: observer.on_completed() - right_d = obs.subscribe_(send_right, on_error_right, on_completed_right, scheduler) + right_d = obs.subscribe_( + send_right, on_error_right, on_completed_right, scheduler + ) right_subscription.disposable = right_d return CompositeDisposable(left_subscription, right_subscription) diff --git a/rx/core/operators/asobservable.py b/rx/core/operators/asobservable.py index 879eb20b0..d429277ca 100644 --- a/rx/core/operators/asobservable.py +++ b/rx/core/operators/asobservable.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, TypeVar, Optional -from rx.core import Observable +from rx.core import Observable, abc +_T = TypeVar("_T") -def _as_observable() -> Callable[[Observable], Observable]: - def as_observable(source: Observable) -> Observable: + +def _as_observable() -> Callable[[Observable[_T]], Observable[_T]]: + def as_observable(source: Observable[_T]) -> Observable[_T]: """Hides the identity of an observable sequence. Args: @@ -15,8 +17,15 @@ def as_observable(source: Observable) -> Observable: source sequence. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: return source.subscribe(observer, scheduler=scheduler) return Observable(subscribe) + return as_observable + + +__all__ = ["_as_observable"] diff --git a/rx/core/operators/debounce.py b/rx/core/operators/debounce.py index 7350861d7..e1f6e3057 100644 --- a/rx/core/operators/debounce.py +++ b/rx/core/operators/debounce.py @@ -1,14 +1,19 @@ -from typing import Any, Callable, TypeVar +from typing import Any, Callable, List, Optional, TypeVar, cast from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.scheduler import TimeoutScheduler _T = TypeVar("_T") -def _debounce(duetime: typing.RelativeTime, scheduler: abc.SchedulerBase) -> Callable[[Observable[_T]], Observable[_T]]: +def _debounce( + duetime: typing.RelativeTime, scheduler: abc.SchedulerBase +) -> Callable[[Observable[_T]], Observable[_T]]: def debounce(source: Observable[_T]) -> Observable[_T]: """Ignores values from an observable sequence which are followed by another value before duetime. @@ -24,14 +29,17 @@ def debounce(source: Observable[_T]) -> Observable[_T]: returns the debounced observable sequence. """ - def subscribe(observer, scheduler_=None) -> abc.DisposableBase: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() cancelable = SerialDisposable() has_value = [False] - value = [None] - _id = [0] + value: List[_T] = [cast(_T, None)] + _id: List[int] = [0] - def on_next(x: Any) -> None: + def on_next(x: _T) -> None: has_value[0] = True value[0] = x _id[0] += 1 @@ -39,7 +47,7 @@ def on_next(x: Any) -> None: d = SingleAssignmentDisposable() cancelable.disposable = d - def action(scheduler, state=None) -> None: + def action(scheduler: abc.SchedulerBase, state: Any = None) -> None: if has_value[0] and _id[0] == current_id: observer.on_next(value[0]) has_value[0] = False @@ -61,7 +69,9 @@ def on_completed() -> None: has_value[0] = False _id[0] += 1 - subscription = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler_) + subscription = source.subscribe_( + on_next, on_error, on_completed, scheduler=scheduler_ + ) return CompositeDisposable(subscription, cancelable) return Observable(subscribe) @@ -88,7 +98,10 @@ def throttle_with_mapper(source: Observable[_T]) -> Observable[_T]: The throttled observable sequence. """ - def subscribe(observer, scheduler=None) -> abc.DisposableBase: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: cancelable = SerialDisposable() has_value = [False] value = [None] @@ -109,7 +122,7 @@ def on_next(x): d = SingleAssignmentDisposable() cancelable.disposable = d - def on_next(x: Any) -> None: + def on_next(x: _T) -> None: if has_value[0] and _id[0] == current_id: observer.on_next(value[0]) @@ -123,9 +136,11 @@ def on_completed() -> None: has_value[0] = False d.dispose() - d.disposable = throttle.subscribe_(on_next, observer.on_error, on_completed, scheduler=scheduler) + d.disposable = throttle.subscribe_( + on_next, observer.on_error, on_completed, scheduler=scheduler + ) - def on_error(e) -> None: + def on_error(e: Exception) -> None: cancelable.dispose() observer.on_error(e) has_value[0] = False @@ -140,9 +155,14 @@ def on_completed() -> None: has_value[0] = False _id[0] += 1 - subscription = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) + subscription = source.subscribe_( + on_next, on_error, on_completed, scheduler=scheduler + ) return CompositeDisposable(subscription, cancelable) return Observable(subscribe) return throttle_with_mapper + + +__all__ = ["_debounce", "_throttle_with_mapper"] diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index ee8958826..dbb9353e0 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -1,5 +1,4 @@ -import collections -from typing import Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, Iterable from rx import from_, from_future from rx import operators as ops @@ -17,10 +16,12 @@ def _flat_map_internal( mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, ) -> Observable[_T2]: def projection(x: _T1, i: int): - mapper_result = mapper(x) if mapper else mapper_indexed(x, i) if mapper_indexed else None + mapper_result = ( + mapper(x) if mapper else mapper_indexed(x, i) if mapper_indexed else None + ) if is_future(mapper_result): result = from_future(mapper_result) - elif isinstance(mapper_result, collections.abc.Iterable): + elif isinstance(mapper_result, Iterable): result = from_(mapper_result) else: result = mapper_result @@ -29,7 +30,9 @@ def projection(x: _T1, i: int): return source.pipe(ops.map_indexed(projection), ops.merge_all()) -def _flat_map(mapper: Optional[Mapper[_T1, Observable[_T2]]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def _flat_map( + mapper: Optional[Mapper[_T1, Observable[_T2]]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map(source: Observable[_T1]) -> Observable[_T2]: """One of the Following: Projects each element of an observable sequence to an observable @@ -89,7 +92,9 @@ def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: return flat_map_indexed -def _flat_map_latest(mapper: Mapper[_T1, Observable[_T2]]) -> Callable[[Observable[_T1]], Observable[_T2]]: +def _flat_map_latest( + mapper: Mapper[_T1, Observable[_T2]] +) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map_latest(source: Observable[_T1]) -> Observable[_T2]: """Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index e96e0d1eb..1e9306bff 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -1,12 +1,14 @@ -from typing import Callable, Optional +from typing import Callable, Optional, Any from rx import operators as ops from rx.core import Observable, pipe -from rx.core.typing import Any, Predicate +from rx.core.typing import Predicate from rx.internal.exceptions import SequenceContainsNoElementsError -def _single_or_default_async(has_default: bool = False, default_value: Any = None) -> Callable[[Observable], Observable]: +def _single_or_default_async( + has_default: bool = False, default_value: Any = None +) -> Callable[[Observable], Observable]: def single_or_default_async(source: Observable) -> Observable: def subscribe(observer, scheduler=None): value = [default_value] @@ -14,7 +16,9 @@ def subscribe(observer, scheduler=None): def on_next(x): if seen_value[0]: - observer.on_error(Exception('Sequence contains more than one element')) + observer.on_error( + Exception("Sequence contains more than one element") + ) else: value[0] = x seen_value[0] = True @@ -26,12 +30,18 @@ def on_completed(): observer.on_next(value[0]) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) + return single_or_default_async -def _single_or_default(predicate: Optional[Predicate] = None, default_value: Any = None) -> Callable[[Observable], Observable]: +def _single_or_default( + predicate: Optional[Predicate] = None, default_value: Any = None +) -> Callable[[Observable], Observable]: """Returns the only element of an observable sequence that matches the predicate, or a default value if no such element exists this method reports an exception if there is more than one element in the diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index ca87d8d3b..715fd96b2 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -1,14 +1,17 @@ +from asyncio import Future from typing import Any, Callable, Optional, TypeVar, Union, cast from rx import from_future -from rx.core import Observable, abc, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import is_future _T = TypeVar("_T") -def _skip_until(other: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: +def _skip_until( + other: Union[Observable[_T], "Future[_T]"] +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after the other observable sequence produces a value. @@ -23,12 +26,15 @@ def _skip_until(other: Union[Observable[_T], typing.Future]) -> Callable[[Observ """ if is_future(other): - obs: Observable[Any] = from_future(cast(typing.Future, other)) + obs: Observable[Any] = from_future(other) else: obs = cast(Observable[_T], other) def skip_until(source: Observable[_T]) -> Observable[_T]: - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): is_open = [False] def on_next(left: _T) -> None: @@ -39,7 +45,9 @@ def on_completed() -> None: if is_open[0]: observer.on_completed() - subs = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + subs = source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) subscriptions = CompositeDisposable(subs) right_subscription = SingleAssignmentDisposable() @@ -52,7 +60,9 @@ def on_next2(x: Any) -> None: def on_completed2(): right_subscription.dispose() - right_subscription.disposable = obs.subscribe_(on_next2, observer.on_error, on_completed2, scheduler) + right_subscription.disposable = obs.subscribe_( + on_next2, observer.on_error, on_completed2, scheduler + ) return subscriptions diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index 91032f971..c36656e8f 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -1,7 +1,8 @@ +from asyncio import Future from typing import Callable, Optional, Union, cast, TypeVar from rx import from_future -from rx.core import Observable, typing, abc +from rx.core import Observable, abc from rx.disposable import CompositeDisposable from rx.internal import noop from rx.internal.utils import is_future @@ -9,7 +10,9 @@ _T = TypeVar("_T") -def _take_until(other: Union[Observable[_T], typing.Future]) -> Callable[[Observable[_T]], Observable[_T]]: +def _take_until( + other: Union[Observable[_T], "Future[_T]"] +) -> Callable[[Observable[_T]], Observable[_T]]: if is_future(other): obs: Observable[_T] = from_future(other) else: @@ -29,13 +32,15 @@ def take_until(source: Observable[_T]) -> Observable[_T]: """ def subscribe( - observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: def on_completed(_: _T) -> None: observer.on_completed() return CompositeDisposable( - source.subscribe(observer), obs.subscribe_(on_completed, observer.on_error, noop, scheduler) + source.subscribe(observer), + obs.subscribe_(on_completed, observer.on_error, noop, scheduler), ) return Observable(subscribe) diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index acae08f3e..12aee8c42 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -1,19 +1,20 @@ from asyncio import Future -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, cast -from rx.core import Observable +from rx.core import Observable, abc from rx.internal.exceptions import SequenceContainsNoElementsError -from .. import abc +_T = TypeVar("_T") def _to_future( - future_ctor: Optional[Callable[[], Future]] = None, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Future]: - future_ctor = future_ctor or Future - future = future_ctor() + future_ctor: Optional[Callable[[], "Future[_T]"]] = None, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], "Future[_T]"]: + future_ctor_: Callable[[], "Future[_T]"] = future_ctor or (lambda: Future()) + future: "Future[_T]" = future_ctor_() - def to_future(source: Observable) -> Future: + def to_future(source: Observable[_T]) -> "Future[_T]": """Converts an existing observable sequence to a Future. If the observable emits a single item, then this item is set as the @@ -31,15 +32,15 @@ def to_future(source: Observable) -> Future: """ has_value = False - last_value = None + last_value = cast(_T, None) - def on_next(value): + def on_next(value: _T): nonlocal last_value nonlocal has_value last_value = value has_value = True - def on_error(err): + def on_error(err: Exception): if not future.cancelled(): future.set_exception(err) @@ -58,3 +59,6 @@ def on_completed(): return future return to_future + + +__all__ = ["_to_future"] diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index 575f547fb..d871e7e7b 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -1,10 +1,13 @@ -from typing import Callable +from typing import Callable, Iterable, List, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc -def _to_iterable() -> Callable[[Observable], Observable]: - def to_iterable(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def _to_iterable() -> Callable[[Observable[_T]], Observable[Iterable[_T]]]: + def to_iterable(source: Observable[_T]) -> Observable[Iterable[_T]]: """Creates an iterable from an observable sequence. Returns: @@ -12,12 +15,16 @@ def to_iterable(source: Observable) -> Observable: iterable containing all the elements of the source sequence. """ - def subscribe(observer, scheduler=None): + + def subscribe( + observer: abc.ObserverBase[Iterable[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ): nonlocal source - queue = [] + queue: List[_T] = [] - def on_next(item): + def on_next(item: _T): queue.append(item) def on_completed(): @@ -26,6 +33,13 @@ def on_completed(): queue = [] observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) - return to_iterable \ No newline at end of file + + return to_iterable + + +__all__ = ["_to_iterable"] diff --git a/rx/core/pipe.py b/rx/core/pipe.py index 06e8208ee..ed81cde3f 100644 --- a/rx/core/pipe.py +++ b/rx/core/pipe.py @@ -11,39 +11,20 @@ @overload -def pipe(*operators: Callable[["Observable"], "Observable"]) -> Callable[["Observable"], "Observable"]: # type: ignore - """Compose multiple operators left to right. - - Composes zero or more operators into a functional composition. The - operators are composed to left to right. A composition of zero - operators gives back the source. - - Examples: - >>> pipe()(source) == source - >>> pipe(f)(source) == f(source) - >>> pipe(f, g)(source) == g(f(source)) - >>> pipe(f, g, h)(source) == h(g(f(source))) - ... - - Returns: - The composed observable. - """ +def pipe(__op1: Callable[[A], B]) -> Callable[[A], B]: ... @overload -def pipe(__op1: Callable[[A], B]) -> Callable[[A], B]: # pylint: disable=function-redefined - ... - - -@overload -def pipe(__op1: Callable[[A], B], __op2: Callable[[B], C]) -> Callable[[A], C]: # pylint: disable=function-redefined +def pipe(__op1: Callable[[A], B], __op2: Callable[[B], C]) -> Callable[[A], C]: ... @overload def pipe( - __op1: Callable[[A], B], __op2: Callable[[B], C], __op3: Callable[[C], D] # pylint: disable=function-redefined + __op1: Callable[[A], B], + __op2: Callable[[B], C], + __op3: Callable[[C], D], ) -> Callable[[A], D]: ... diff --git a/rx/core/typing.py b/rx/core/typing.py index 2c742fa42..65e0d4bb2 100644 --- a/rx/core/typing.py +++ b/rx/core/typing.py @@ -1,26 +1,20 @@ from threading import Thread -from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union +from typing import Callable, TypeVar, Union from .abc.observable import Subscription from .abc.observer import OnCompleted, OnError, OnNext -from .abc.periodicscheduler import (ScheduledPeriodicAction, - ScheduledSingleOrPeriodicAction) -from .abc.scheduler import (AbsoluteOrRelativeTime, AbsoluteTime, RelativeTime, - ScheduledAction) +from .abc.periodicscheduler import ( + ScheduledPeriodicAction, + ScheduledSingleOrPeriodicAction, +) +from .abc.scheduler import ( + AbsoluteOrRelativeTime, + AbsoluteTime, + RelativeTime, + ScheduledAction, +) from .abc.startable import StartableBase -if TYPE_CHECKING: - # Futures cannot take generic argument before Python 3.9 - class Future: - """Mock future for type testing.""" - - result: Any = None - add_done_callback: Any = None - cancel: Any = None - -else: - from asyncio.futures import Future - _TState = TypeVar("_TState") # Can be anything _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") @@ -31,7 +25,7 @@ class Future: MapperIndexed = Callable[[_T1, int], _T2] Predicate = Callable[[_T1], bool] PredicateIndexed = Callable[[_T1, int], bool] -Comparer = Callable[[_T1, _T2], bool] +Comparer = Callable[[_T1, _T1], bool] SubComparer = Callable[[_T1, _T2], int] Accumulator = Callable[[_TState, _T1], _TState] @@ -59,5 +53,4 @@ class Future: "Startable", "StartableTarget", "Subscription", - "Future", ] diff --git a/rx/internal/utils.py b/rx/internal/utils.py index 95fbfca45..9c5c0aaa4 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -1,22 +1,25 @@ +from asyncio import Future from functools import update_wrapper from types import FunctionType from typing import Any, Callable, Iterable, Optional, cast -from rx.core import abc, typing +from rx.core import abc from rx.disposable import CompositeDisposable def add_ref(xs: abc.ObservableBase[Any], r): from rx.core import Observable - def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ): return CompositeDisposable(r.disposable, xs.subscribe(observer)) return Observable(subscribe) def is_future(fut: Any) -> bool: - return isinstance(fut, typing.Future) + return isinstance(fut, Future) def infinite() -> Iterable[int]: diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 90ae45835..41bff3f24 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -3,18 +3,33 @@ from asyncio import Future from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar, Union, cast -from rx.core import ConnectableObservable, GroupedObservable, Observable, abc, pipe, typing -from rx.core.typing import Accumulator, Comparer, Mapper, MapperIndexed, Predicate, PredicateIndexed +from rx.core import ( + ConnectableObservable, + GroupedObservable, + Observable, + abc, + pipe, + typing, +) +from rx.core.typing import ( + Accumulator, + Comparer, + Mapper, + MapperIndexed, + Predicate, + PredicateIndexed, +) from rx.internal.utils import NotSet from rx.subject import Subject _T = TypeVar("_T") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") +_TKey = TypeVar("_TKey") _TState = TypeVar("_TState") -def all(predicate: Predicate) -> Callable[[Observable[_T]], Observable[_T]]: +def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: """Determines whether all elements of an observable sequence satisfy a condition. @@ -139,7 +154,9 @@ def buffer(boundaries: Observable) -> Callable[[Observable], Observable]: return _buffer(boundaries) -def buffer_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observable], Observable]: +def buffer_when( + closing_mapper: Callable[[], Observable] +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more buffers. @@ -207,7 +224,9 @@ def buffer_toggle( return _buffer_toggle(openings, closing_mapper) -def buffer_with_count(count: int, skip: Optional[int] = None) -> Callable[[Observable], Observable]: +def buffer_with_count( + count: int, skip: Optional[int] = None +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more buffers which are produced based on element count information. @@ -277,7 +296,9 @@ def buffer_with_time( return _buffer_with_time(timespan, timeshift, scheduler) -def buffer_with_time_or_count(timespan, count, scheduler=None) -> Callable[[Observable], Observable]: +def buffer_with_time_or_count( + timespan, count, scheduler=None +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into a buffer that is completed when either it's full or a given amount of time has elapsed. @@ -397,7 +418,9 @@ def concat(*sources: Observable) -> Callable[[Observable], Observable]: return _concat(*sources) -def contains(value: Any, comparer: Optional[typing.Comparer] = None) -> Callable[[Observable], Observable]: +def contains( + value: Any, comparer: Optional[typing.Comparer] = None +) -> Callable[[Observable], Observable]: """Determines whether an observable sequence contains a specified element with an optional equality comparer. @@ -427,7 +450,9 @@ def contains(value: Any, comparer: Optional[typing.Comparer] = None) -> Callable return _contains(value, comparer) -def count(predicate: Optional[typing.Predicate] = None) -> Callable[[Observable], Observable]: +def count( + predicate: Optional[typing.Predicate] = None, +) -> Callable[[Observable], Observable]: """Returns an observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items. @@ -461,7 +486,7 @@ def count(predicate: Optional[typing.Predicate] = None) -> Callable[[Observable] def debounce( duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Ignores values from an observable sequence which are followed by another value before duetime. @@ -523,7 +548,8 @@ def default_if_empty(default_value: Any = None) -> Callable[[Observable], Observ def delay_subscription( - duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + duetime: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable], Observable]: """Time shifts the observable sequence by delaying the subscription. @@ -552,7 +578,9 @@ def delay_subscription( return _delay_subscription(duetime, scheduler=scheduler) -def delay_with_mapper(subscription_delay=None, delay_duration_mapper=None) -> Callable[[Observable], Observable]: +def delay_with_mapper( + subscription_delay=None, delay_duration_mapper=None +) -> Callable[[Observable], Observable]: """Time shifts the observable sequence based on a subscription delay and a delay mapper function for each element. @@ -671,8 +699,9 @@ def distinct( def distinct_until_changed( - key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: + key_mapper: Optional[Mapper[_T, _TKey]] = None, + comparer: Optional[Comparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns an observable sequence that contains only distinct contiguous elements according to the key_mapper and the comparer. @@ -828,7 +857,9 @@ def element_at(index: int) -> Callable[[Observable[_T]], Observable[_T]]: return _element_at_or_default(index, False) -def element_at_or_default(index: int, default_value: Any = None) -> Callable[[Observable[_T]], Observable[_T]]: +def element_at_or_default( + index: int, default_value: Any = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the element at a specified index in a sequence or a default value if the index is out of range. @@ -1113,7 +1144,9 @@ def first_or_default( return _first_or_default(predicate, default_value) -def flat_map(mapper: Optional[Mapper[_T1, Observable[_T2]]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def flat_map( + mapper: Optional[Mapper[_T1, Observable[_T2]]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: """The flat_map operator. .. marble:: @@ -1457,7 +1490,9 @@ def join( return _join(right, left_duration_mapper, right_duration_mapper) -def last(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def last( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """The last operator. Returns the last element of an observable sequence that satisfies @@ -1529,7 +1564,9 @@ def last_or_default( return _last_or_default(predicate, default_value) -def map(mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def map( + mapper: Optional[Mapper[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: """The map operator. Project each element of an observable sequence into a new form. @@ -1633,7 +1670,9 @@ def max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl return _max(comparer) -def max_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def max_by( + key_mapper: Mapper, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: """The max_by operator. Returns the elements in an observable sequence with the maximum @@ -1664,7 +1703,9 @@ def max_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[ return _max_by(key_mapper, comparer) -def merge(*sources: Observable, max_concurrent: Optional[int] = None) -> Callable[[Observable], Observable]: +def merge( + *sources: Observable, max_concurrent: Optional[int] = None +) -> Callable[[Observable], Observable]: """Merges an observable sequence of observable sequences into an observable sequence, limiting the number of concurrent subscriptions to inner sequences. Or merges two observable @@ -1751,7 +1792,9 @@ def min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl return _min(comparer) -def min_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def min_by( + key_mapper: Mapper, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: """The `min_by` operator. Returns the elements in an observable sequence with the minimum key @@ -1784,7 +1827,9 @@ def min_by(key_mapper: Mapper, comparer: Optional[Comparer] = None) -> Callable[ def multicast( subject: Optional[abc.SubjectBase] = None, - subject_factory: Optional[Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase]] = None, + subject_factory: Optional[ + Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase] + ] = None, mapper: Optional[Callable[[ConnectableObservable], Observable]] = None, ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: """Multicasts the source sequence notifications through an @@ -1919,7 +1964,9 @@ def partition(predicate: Predicate) -> Callable[[Observable], List[Observable]]: return _partition(predicate) -def partition_indexed(predicate_indexed: PredicateIndexed) -> Callable[[Observable], List[Observable]]: +def partition_indexed( + predicate_indexed: PredicateIndexed, +) -> Callable[[Observable], List[Observable]]: """The indexed partition operator. Returns two observables which partition the observations of the @@ -1989,7 +2036,9 @@ def pluck_attr(prop: str) -> Callable[[Observable], Observable]: return _pluck_attr(prop) -def publish(mapper: Optional[Mapper] = None) -> Callable[[Observable], ConnectableObservable]: +def publish( + mapper: Optional[Mapper] = None, +) -> Callable[[Observable], ConnectableObservable]: """The `publish` operator. Returns an observable sequence that is the result of invoking the @@ -2020,7 +2069,9 @@ def publish(mapper: Optional[Mapper] = None) -> Callable[[Observable], Connectab return _publish(mapper) -def publish_value(initial_value: Any, mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def publish_value( + initial_value: Any, mapper: Optional[Mapper] = None +) -> Callable[[Observable], Observable]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence and starts with @@ -2104,7 +2155,9 @@ def ref_count() -> Callable[[ConnectableObservable], Observable]: return _ref_count() -def repeat(repeat_count: Optional[int] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def repeat( + repeat_count: Optional[int] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the observable sequence a specified number of times. If the repeat count is not specified, the sequence repeats indefinitely. @@ -2179,7 +2232,9 @@ def replay( return _replay(mapper, buffer_size, window, scheduler=scheduler) -def retry(retry_count: Optional[int] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def retry( + retry_count: Optional[int] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the source observable sequence the specified number of times or until it successfully terminates. If the retry count is not specified, it retries indefinitely. @@ -2202,7 +2257,8 @@ def retry(retry_count: Optional[int] = None) -> Callable[[Observable[_T]], Obser def sample( - sampler: Union[typing.RelativeTime, Observable[Any]], scheduler: Optional[abc.SchedulerBase] = None + sampler: Union[typing.RelativeTime, Observable[Any]], + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Samples the observable sequence at each interval. @@ -2268,7 +2324,9 @@ def scan( return _scan(accumulator, seed) -def sequence_equal(second: Observable, comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def sequence_equal( + second: Observable, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: """Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer. @@ -2322,7 +2380,9 @@ def share() -> Callable[[Observable[_T]], Observable[_T]]: return _share() -def single(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def single( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """The single operator. Returns the only element of an observable sequence that satisfies @@ -2519,7 +2579,8 @@ def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[ def skip_until_with_time( - start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + start_time: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements from the observable source sequence until the specified start time. @@ -2552,7 +2613,9 @@ def skip_until_with_time( return _skip_until_with_time(start_time, scheduler=scheduler) -def skip_while(predicate: typing.Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_while( + predicate: typing.Predicate[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: """The `skip_while` operator. Bypasses elements in an observable sequence as long as a specified @@ -2585,7 +2648,9 @@ def skip_while(predicate: typing.Predicate[_T]) -> Callable[[Observable[_T]], Ob return _skip_while(predicate) -def skip_while_indexed(predicate: typing.PredicateIndexed[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_while_indexed( + predicate: typing.PredicateIndexed[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: """Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. The element's index is used in the logic of the predicate @@ -2694,7 +2759,9 @@ def slice( return _slice(start, stop, step) -def some(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[bool]]: +def some( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[bool]]: """The some operator. Determines whether some element of an observable sequence @@ -2727,7 +2794,9 @@ def some(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]] return _some(predicate) -def starmap(mapper: Optional[Callable[..., _T]] = None) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: +def starmap( + mapper: Optional[Callable[..., _T]] = None +) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: """The starmap operator. Unpack arguments grouped as tuple elements of an observable @@ -2845,7 +2914,9 @@ def subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observa return _subscribe_on(scheduler) -def sum(key_mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def sum( + key_mapper: Optional[Mapper[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: """Computes the sum of a sequence of values that are obtained by invoking an optional transform function on each element of the input sequence, else if not specified computes the sum on each item @@ -3064,7 +3135,8 @@ def take_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[ def take_until_with_time( - end_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + end_time: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Takes elements for the specified duration until the specified end time, using the specified scheduler to run timers. @@ -3097,7 +3169,9 @@ def take_until_with_time( return _take_until_with_time(end_time, scheduler=scheduler) -def take_while(predicate: Predicate, inclusive: bool = False) -> Callable[[Observable], Observable]: +def take_while( + predicate: Predicate, inclusive: bool = False +) -> Callable[[Observable], Observable]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -3129,7 +3203,9 @@ def take_while(predicate: Predicate, inclusive: bool = False) -> Callable[[Obser return _take_while(predicate, inclusive) -def take_while_indexed(predicate: PredicateIndexed, inclusive: bool = False) -> Callable[[Observable], Observable]: +def take_while_indexed( + predicate: PredicateIndexed, inclusive: bool = False +) -> Callable[[Observable], Observable]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -3218,7 +3294,9 @@ def throttle_first( return _throttle_first(window_duration, scheduler) -def throttle_with_mapper(throttle_duration_mapper: Callable[[Any], Observable]) -> Callable[[Observable], Observable]: +def throttle_with_mapper( + throttle_duration_mapper: Callable[[Any], Observable] +) -> Callable[[Observable], Observable]: """The throttle_with_mapper operator. Ignores values from an observable sequence which are followed by @@ -3241,7 +3319,9 @@ def throttle_with_mapper(throttle_duration_mapper: Callable[[Any], Observable]) return _throttle_with_mapper(throttle_duration_mapper) -def timestamp(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: +def timestamp( + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: """The timestamp operator. Records the timestamp for each value in an observable sequence. @@ -3263,7 +3343,9 @@ def timestamp(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observ def timeout( - duetime: typing.AbsoluteTime, other: Optional[Observable] = None, scheduler: Optional[abc.SchedulerBase] = None + duetime: typing.AbsoluteTime, + other: Optional[Observable] = None, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable], Observable]: """Returns the source observable sequence or the other observable sequence if duetime elapses. @@ -3332,7 +3414,9 @@ def timeout_with_mapper( return _timeout_with_mapper(first_timeout, timeout_duration_mapper, other) -def time_interval(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: +def time_interval( + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable], Observable]: """Records the time interval between consecutive values in an observable sequence. @@ -3356,7 +3440,9 @@ def time_interval(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Ob return _time_interval(scheduler=scheduler) -def to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def to_dict( + key_mapper: Mapper, element_mapper: Optional[Mapper] = None +) -> Callable[[Observable], Observable]: """Converts the observable sequence to a Map if it exists. Args: @@ -3376,7 +3462,9 @@ def to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None) -> Call return _to_dict(key_mapper, element_mapper) -def to_future(future_ctor: Optional[Callable[[], Future]] = None) -> Callable[[Observable], Future]: +def to_future( + future_ctor: Optional[Callable[[], Future]] = None +) -> Callable[[Observable], Future]: """Converts an existing observable sequence to a Future. Example: @@ -3495,7 +3583,9 @@ def window(boundaries: Observable) -> Callable[[Observable], Observable]: return _window(boundaries) -def window_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observable], Observable]: +def window_when( + closing_mapper: Callable[[], Observable] +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more windows. @@ -3567,7 +3657,9 @@ def window_toggle( return _window_toggle(openings, closing_mapper) -def window_with_count(count: int, skip: Optional[int] = None) -> Callable[[Observable], Observable]: +def window_with_count( + count: int, skip: Optional[int] = None +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more windows which are produced based on element count information. @@ -3609,7 +3701,9 @@ def window_with_time( def window_with_time_or_count( - timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None + timespan: typing.RelativeTime, + count: int, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable], Observable]: from rx.core.operators.windowwithtimeorcount import _window_with_time_or_count @@ -3677,7 +3771,9 @@ def zip(*args: Observable) -> Callable[[Observable], Observable]: return _zip(*args) -def zip_with_iterable(second: Iterable[_T2]) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: +def zip_with_iterable( + second: Iterable[_T2], +) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: """Merges the specified observable sequence and list into one observable sequence by creating a tuple whenever all of the observable sequences have produced an element at a diff --git a/rx/scheduler/eventloop/asynciothreadsafescheduler.py b/rx/scheduler/eventloop/asynciothreadsafescheduler.py index e3cb92fc5..4dbb7374f 100644 --- a/rx/scheduler/eventloop/asynciothreadsafescheduler.py +++ b/rx/scheduler/eventloop/asynciothreadsafescheduler.py @@ -4,8 +4,7 @@ from typing import TYPE_CHECKING, List, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from .asyncioscheduler import AsyncIOScheduler @@ -29,7 +28,9 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: super().__init__(loop) - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -52,10 +53,7 @@ def dispose() -> None: handle.cancel() return - if TYPE_CHECKING: - future: Future[int] = Future() - else: - future: Future = Future() + future: "Future[int]" = Future() def cancel_handle() -> None: handle.cancel() @@ -67,7 +65,10 @@ def cancel_handle() -> None: return CompositeDisposable(sad, Disposable(dispose)) def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -110,10 +111,7 @@ def do_cancel_handles(): do_cancel_handles() return - if TYPE_CHECKING: - future: Future[int] = Future() - else: - future: Future = Future() + future: "Future[int]" = Future() def cancel_handle() -> None: do_cancel_handles() @@ -125,7 +123,10 @@ def cancel_handle() -> None: return CompositeDisposable(sad, Disposable(dispose)) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/tests/test_observable/test_flatmap_async.py b/tests/test_observable/test_flatmap_async.py index f568638e6..5a9e8455d 100644 --- a/tests/test_observable/test_flatmap_async.py +++ b/tests/test_observable/test_flatmap_async.py @@ -7,25 +7,29 @@ class TestFlatMapAsync(unittest.TestCase): - def test_flat_map_async(self): actual_next = None loop = asyncio.get_event_loop() scheduler = AsyncIOScheduler(loop=loop) - def mapper(i): - async def _mapper(i): + def mapper(i: int): + async def _mapper(i: int): return i + 1 return asyncio.ensure_future(_mapper(i)) - def on_next(i): + def on_next(i: int): nonlocal actual_next actual_next = i + def on_error(ex): + print("Error", ex) + async def test_flat_map(): - x = Subject() - x.pipe(ops.flat_map(mapper)).subscribe(on_next, scheduler=scheduler) + x: Subject[int] = Subject() + x.pipe(ops.flat_map(mapper)).subscribe( + on_next, on_error, scheduler=scheduler + ) x.on_next(10) await asyncio.sleep(0.1) From 5a2114d3c1ad9f79ccbd60c800b65151ab1d6191 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 09:58:32 +0100 Subject: [PATCH 007/103] Add more type hints --- examples/timeflies/timeflies_tkinter.py | 12 ++-- rx/__init__.py | 85 ++++++++++++++++++------- rx/core/abc/asyncobserver.py | 3 + rx/core/abc/disposable.py | 3 + rx/core/abc/periodicscheduler.py | 16 ++++- rx/core/abc/scheduler.py | 27 ++++++-- rx/core/abc/startable.py | 3 + rx/core/abc/subject.py | 8 ++- rx/core/observable/empty.py | 9 ++- rx/core/observable/ifthen.py | 3 + rx/core/observable/observable.py | 28 ++++---- rx/core/observable/onerrorresumenext.py | 2 +- rx/core/observable/repeat.py | 11 +++- rx/core/observable/start.py | 8 ++- rx/core/observable/startasync.py | 9 ++- rx/core/observable/throw.py | 13 +++- rx/core/observable/withlatestfrom.py | 21 ++++-- rx/core/operators/buffer.py | 44 ++++++++----- rx/core/operators/combinelatest.py | 14 ++-- rx/core/operators/concat.py | 12 +++- rx/core/operators/contains.py | 13 ++-- rx/core/operators/count.py | 11 +++- rx/core/operators/window.py | 81 +++++++++++++++-------- rx/internal/utils.py | 3 +- rx/operators/__init__.py | 47 +++++++++----- 25 files changed, 347 insertions(+), 139 deletions(-) diff --git a/examples/timeflies/timeflies_tkinter.py b/examples/timeflies/timeflies_tkinter.py index 590cf25d1..f5b0c2cb7 100644 --- a/examples/timeflies/timeflies_tkinter.py +++ b/examples/timeflies/timeflies_tkinter.py @@ -1,5 +1,5 @@ from typing import Any, Tuple -from tkinter import Tk, Label, Frame +from tkinter import Tk, Label, Frame, Event import tkinter import rx @@ -13,7 +13,7 @@ def main(): root.title("Rx for Python rocks") scheduler = TkinterScheduler(root) - mousemove: Subject[tkinter.Event[Any]] = Subject() + mousemove: Subject[Event[Any]] = Subject() frame = Frame(root, width=600, height=600) @@ -25,7 +25,9 @@ def on_next(info: Tuple[tkinter.Label, tkinter.Event[Any], int]): label, ev, i = info label.place(x=ev.x + i * 12 + 15, y=ev.y) - def handle_label(label: tkinter.Label, i: int) -> Observable[Tuple[tkinter.Label, tkinter.Event[Any], int]]: + def handle_label( + label: tkinter.Label, i: int + ) -> Observable[Tuple[tkinter.Label, tkinter.Event[Any], int]]: label.config(dict(borderwidth=0, padx=0, pady=0)) mapper = ops.map(lambda ev: (label, ev, i)) @@ -36,7 +38,9 @@ def handle_label(label: tkinter.Label, i: int) -> Observable[Tuple[tkinter.Label mapper = ops.map(lambda c: Label(frame, text=c)) labeler = ops.flat_map_indexed(handle_label) - rx.from_(text).pipe(mapper, labeler).subscribe(on_next, on_error=print, scheduler=scheduler) + rx.from_(text).pipe(mapper, labeler).subscribe( + on_next, on_error=print, scheduler=scheduler + ) frame.pack() root.mainloop() diff --git a/rx/__init__.py b/rx/__init__.py index b64e5e6d1..b45bdf084 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1,7 +1,17 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin from asyncio import Future -from typing import Any, Callable, Iterable, Mapping, Optional, Tuple, TypeVar, Union +from typing import ( + Any, + Callable, + Iterable, + Mapping, + Optional, + Tuple, + TypeVar, + Union, + overload, +) from .core import Observable, abc, pipe, typing from .internal.utils import alias @@ -247,7 +257,7 @@ def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: def defer( - factory: Callable[[abc.SchedulerBase], Union[Observable[_T], Future]] + 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. @@ -420,7 +430,7 @@ def from_callback( return _from_callback(func, mapper) -def from_future(future: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: +def from_future(future: "Future[_T]") -> Observable[_T]: """Converts a Future to an Observable sequence .. marble:: @@ -480,9 +490,9 @@ def from_marbles( string: str, timespan: typing.RelativeTime = 0.1, scheduler: Optional[abc.SchedulerBase] = None, - lookup: Optional[Mapping] = None, + lookup: Optional[Mapping[str, Any]] = None, error: Optional[Exception] = None, -) -> Observable: +) -> Observable[Any]: """Convert a marble diagram string to a cold observable sequence, using an optional scheduler to enumerate the events. @@ -695,9 +705,9 @@ def hot( def if_then( condition: Callable[[], bool], - then_source: Union[Observable, Future], - else_source: Union[None, Observable, Future] = None, -) -> Observable: + 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. .. marble:: @@ -733,7 +743,7 @@ def if_then( def interval( period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Observable: +) -> Observable[int]: """Returns an observable sequence that produces a value after each period. .. marble:: @@ -761,7 +771,7 @@ def interval( return _interval(period, scheduler) -def merge(*sources: Observable[_T]) -> Observable[_T]: +def merge(*sources: Observable[Any]) -> Observable[Any]: """Merges all the observable sequences into a single observable sequence. .. marble:: @@ -805,7 +815,7 @@ def never() -> Observable[Any]: return _never() -def of(*args: Any) -> Observable: +def of(*args: _T) -> Observable[_T]: """This method creates a new observable sequence whose elements are taken from the arguments. @@ -832,7 +842,9 @@ def of(*args: Any) -> Observable: return from_iterable(args) -def on_error_resume_next(*sources: Union[Observable, Future]) -> Observable: +def on_error_resume_next( + *sources: Union[Observable[_T], "Future[_T]"] +) -> Observable[_T]: """Continues an observable sequence that is terminated normally or by an exception with the next observable sequence. @@ -866,7 +878,7 @@ def range( stop: Optional[int] = None, step: Optional[int] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Observable: +) -> Observable[int]: """Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to send out observer messages. @@ -899,8 +911,8 @@ def range( def return_value( - value: Any, scheduler: Optional[abc.SchedulerBase] = None -) -> Observable: + 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'. @@ -929,7 +941,9 @@ def return_value( just = alias("just", "Alias for :func:`rx.return_value`.", return_value) -def repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Observable: +def repeat_value( + value: _T = None, repeat_count: Optional[int] = None +) -> Observable[_T]: """Generates an observable sequence that repeats the given element the specified number of times. @@ -957,7 +971,9 @@ def repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Obser return _repeat_value(value, repeat_count) -def start(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: +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. @@ -992,7 +1008,7 @@ def start(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Obse return _start(func, scheduler) -def start_async(function_async: Callable[[], Future]) -> Observable: +def start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: """Invokes the asynchronous function, surfacing the result through an observable sequence. @@ -1016,8 +1032,8 @@ def start_async(function_async: Callable[[], Future]) -> Observable: def throw( - exception: Exception, scheduler: Optional[abc.SchedulerBase] = None -) -> Observable: + exception: Union[str, Exception], scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[Any]: """Returns an observable sequence that terminates with an exception, using the specified scheduler to send out the single OnError message. @@ -1202,14 +1218,35 @@ def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: __all__ = [ + "amb", + "case", + "catch", + "catch_with_iterable", + "create", + "combine_latest", + "concat", + "concat_with_iterable", + "defer", + "empty", + "fork_join", + "from_callable", + "from_callback", + "from_future", + "from_iterable", "never", + "on_error_resume_next", + "of", + "return_value", "pipe", - "with_latest_from", + "range", + "repeat_value", + "Subject", + "start", + "start_async", "throw", "timer", - "using", "to_async", + "using", + "with_latest_from", "zip", - "return_value", - "Subject", ] diff --git a/rx/core/abc/asyncobserver.py b/rx/core/abc/asyncobserver.py index ed78d08cc..a2bf79d05 100644 --- a/rx/core/abc/asyncobserver.py +++ b/rx/core/abc/asyncobserver.py @@ -22,3 +22,6 @@ async def on_completed_async(self): async def subscribe_async(self, observer): return self + + +__all__ = ["AsyncObserver"] diff --git a/rx/core/abc/disposable.py b/rx/core/abc/disposable.py index c41d3ab83..250c06daf 100644 --- a/rx/core/abc/disposable.py +++ b/rx/core/abc/disposable.py @@ -26,3 +26,6 @@ def __exit__( ): """Context management protocol.""" self.dispose() + + +__all__ = ["DisposableBase"] diff --git a/rx/core/abc/periodicscheduler.py b/rx/core/abc/periodicscheduler.py index 9a34fa453..5d19bc7de 100644 --- a/rx/core/abc/periodicscheduler.py +++ b/rx/core/abc/periodicscheduler.py @@ -7,7 +7,9 @@ _TState = TypeVar("_TState") # Can be anything ScheduledPeriodicAction = Callable[[Optional[_TState]], Optional[_TState]] -ScheduledSingleOrPeriodicAction = Union[ScheduledAction[_TState], ScheduledPeriodicAction[_TState]] +ScheduledSingleOrPeriodicAction = Union[ + ScheduledAction[_TState], ScheduledPeriodicAction[_TState] +] class PeriodicSchedulerBase(ABC): @@ -17,7 +19,10 @@ class PeriodicSchedulerBase(ABC): @abstractmethod def schedule_periodic( - self, period: RelativeTime, action: ScheduledPeriodicAction[_TState], state: Optional[_TState] = None + self, + period: RelativeTime, + action: ScheduledPeriodicAction[_TState], + state: Optional[_TState] = None, ) -> DisposableBase: """Schedules a periodic piece of work. @@ -36,4 +41,9 @@ def schedule_periodic( return NotImplemented -__all__ = ["PeriodicSchedulerBase", "ScheduledPeriodicAction", "ScheduledSingleOrPeriodicAction", "RelativeTime"] +__all__ = [ + "PeriodicSchedulerBase", + "ScheduledPeriodicAction", + "ScheduledSingleOrPeriodicAction", + "RelativeTime", +] diff --git a/rx/core/abc/scheduler.py b/rx/core/abc/scheduler.py index b7c0f7cdd..97500c650 100644 --- a/rx/core/abc/scheduler.py +++ b/rx/core/abc/scheduler.py @@ -9,7 +9,9 @@ AbsoluteTime = Union[datetime, float] RelativeTime = Union[timedelta, float] AbsoluteOrRelativeTime = Union[datetime, timedelta, float] -ScheduledAction = Callable[["SchedulerBase", Optional[_TState]], Optional[DisposableBase]] +ScheduledAction = Callable[ + ["SchedulerBase", Optional[_TState]], Optional[DisposableBase] +] class SchedulerBase(ABC): @@ -31,7 +33,9 @@ def now(self) -> datetime: return NotImplemented @abstractmethod - def schedule(self, action: ScheduledAction[_TState], state: Optional[_TState] = None) -> DisposableBase: + def schedule( + self, action: ScheduledAction[_TState], state: Optional[_TState] = None + ) -> DisposableBase: """Schedules an action to be executed. Args: @@ -47,7 +51,10 @@ def schedule(self, action: ScheduledAction[_TState], state: Optional[_TState] = @abstractmethod def schedule_relative( - self, duetime: RelativeTime, action: ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: RelativeTime, + action: ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> DisposableBase: """Schedules an action to be executed after duetime. @@ -65,7 +72,10 @@ def schedule_relative( @abstractmethod def schedule_absolute( - self, duetime: AbsoluteTime, action: ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: AbsoluteTime, + action: ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> DisposableBase: """Schedules an action to be executed at duetime. @@ -130,3 +140,12 @@ def to_timedelta(cls, value: AbsoluteOrRelativeTime) -> timedelta: """ return NotImplemented + + +__all__ = [ + "SchedulerBase", + "AbsoluteTime", + "RelativeTime", + "AbsoluteOrRelativeTime", + "ScheduledAction", +] diff --git a/rx/core/abc/startable.py b/rx/core/abc/startable.py index b8668cad9..031bbf1a0 100644 --- a/rx/core/abc/startable.py +++ b/rx/core/abc/startable.py @@ -9,3 +9,6 @@ class StartableBase(ABC): @abstractmethod def start(self) -> None: raise NotImplementedError + + +__all__ = ["StartableBase"] diff --git a/rx/core/abc/subject.py b/rx/core/abc/subject.py index d31b5ac05..954fde9b6 100644 --- a/rx/core/abc/subject.py +++ b/rx/core/abc/subject.py @@ -20,7 +20,10 @@ class SubjectBase(ObserverBase[_T], ObservableBase[_T]): @abstractmethod def subscribe( - self, observer: Optional[ObserverBase[_T]] = None, *, scheduler: Optional[SchedulerBase] = None + self, + observer: Optional[ObserverBase[_T]] = None, + *, + scheduler: Optional[SchedulerBase] = None ) -> DisposableBase: """Subscribe an observer to the observable sequence. @@ -62,3 +65,6 @@ def on_completed(self) -> None: """Notifies the observer of the end of the sequence.""" raise NotImplementedError + + +__all__ = ["SubjectBase"] diff --git a/rx/core/observable/empty.py b/rx/core/observable/empty.py index 6068cc0de..48fb685d6 100644 --- a/rx/core/observable/empty.py +++ b/rx/core/observable/empty.py @@ -4,8 +4,10 @@ from rx.scheduler import ImmediateScheduler -def _empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable: - def subscribe(observer: abc.ObserverBase, scheduler_: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: +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() @@ -15,3 +17,6 @@ def action(_: abc.SchedulerBase, __: Any) -> None: return _scheduler.schedule(action) return Observable(subscribe) + + +__all__ = ["_empty"] diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index 58426d2a5..db59a74a4 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -46,3 +46,6 @@ def factory(_: abc.SchedulerBase) -> Observable[_T]: return then_source if condition() else else_source return rx.defer(factory) + + +__all__ = ["_if_then"] diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 18f9f27ac..55043f069 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -3,7 +3,7 @@ import asyncio import threading -from typing import Any, Callable, Optional, TypeVar, Union, cast, overload +from typing import Any, Callable, Iterable, Optional, TypeVar, Union, cast, overload from rx.core import abc from rx.disposable import Disposable @@ -42,7 +42,9 @@ def __init__(self, subscribe: Optional[abc.Subscription[_T]] = None) -> None: self._subscribe = subscribe def _subscribe_core( - self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: return self._subscribe(observer, scheduler) if self._subscribe else Disposable() @@ -147,13 +149,19 @@ def subscribe_( the observable sequence. """ - auto_detach_observer: AutoDetachObserver[_T] = AutoDetachObserver(on_next, on_error, on_completed) + auto_detach_observer: AutoDetachObserver[_T] = AutoDetachObserver( + on_next, on_error, on_completed + ) - def fix_subscriber(subscriber: Union[abc.DisposableBase, Callable[[], None]]) -> abc.DisposableBase: + 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"): + if isinstance(subscriber, abc.DisposableBase) or hasattr( + subscriber, "dispose" + ): return subscriber return Disposable(subscriber) @@ -208,10 +216,6 @@ def pipe( """ ... - @overload - def pipe(self) -> Observable[_T]: - ... - @overload def pipe(self, __op1: Callable[[Observable[_T]], _A]) -> _A: ... @@ -279,7 +283,6 @@ def pipe( ) -> _G: ... - # pylint: disable=function-redefined def pipe(self, *operators: Callable[[Any], Any]) -> Any: """Compose multiple operators left to right. @@ -325,7 +328,7 @@ def run(self) -> Any: return run(self) - def __await__(self) -> Any: + def __await__(self) -> Iterable[_T]: """Awaits the given observable. Returns: @@ -418,3 +421,6 @@ def __getitem__(self, key: slice) -> Observable[_T]: from ..operators.slice import _slice return _slice(start, stop, step)(self) + + +__all__ = ["Observable"] diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 73c8bdd4c..88b4419f0 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -2,7 +2,7 @@ from typing import Optional, Union, TypeVar, Any import rx -from rx.core import Observable, typing, abc +from rx.core import Observable, abc from rx.disposable import ( CompositeDisposable, SerialDisposable, diff --git a/rx/core/observable/repeat.py b/rx/core/observable/repeat.py index 239761adf..0051fc8c8 100644 --- a/rx/core/observable/repeat.py +++ b/rx/core/observable/repeat.py @@ -1,11 +1,15 @@ -from typing import Any, Optional +from typing import TypeVar, Optional import rx from rx import operators as ops from rx.core import Observable +_T = TypeVar("_T") -def _repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Observable: + +def _repeat_value( + value: _T = None, repeat_count: Optional[int] = None +) -> Observable[_T]: """Generates an observable sequence that repeats the given element the specified number of times. @@ -28,3 +32,6 @@ def _repeat_value(value: Any = None, repeat_count: Optional[int] = None) -> Obse xs = rx.return_value(value) return xs.pipe(ops.repeat(repeat_count)) + + +__all__ = ["_repeat_value"] diff --git a/rx/core/observable/start.py b/rx/core/observable/start.py index ddefa4bbb..f3974e28f 100644 --- a/rx/core/observable/start.py +++ b/rx/core/observable/start.py @@ -1,10 +1,14 @@ -from typing import Any, Callable, Optional +from typing import TypeVar, Callable, Optional from rx import to_async from rx.core import Observable, abc +_T = TypeVar("_T") -def _start(func: Callable[..., None], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: + +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. diff --git a/rx/core/observable/startasync.py b/rx/core/observable/startasync.py index 0bf8fd88b..2ae9dc0aa 100644 --- a/rx/core/observable/startasync.py +++ b/rx/core/observable/startasync.py @@ -1,14 +1,19 @@ from asyncio import Future -from typing import Callable +from typing import Callable, TypeVar from rx import from_future, throw from rx.core import Observable +_T = TypeVar("_T") -def _start_async(function_async: Callable[[], Future]) -> Observable: + +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"] diff --git a/rx/core/observable/throw.py b/rx/core/observable/throw.py index 74567436e..cc7de16b9 100644 --- a/rx/core/observable/throw.py +++ b/rx/core/observable/throw.py @@ -1,13 +1,17 @@ -from typing import Any, Optional +from typing import Any, Optional, Union from rx.core import Observable, abc from rx.scheduler import ImmediateScheduler -def _throw(exception: Exception, scheduler: Optional[abc.SchedulerBase] = None) -> Observable: +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, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + 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): @@ -16,3 +20,6 @@ def action(scheduler: abc.SchedulerBase, state: Any): return _scheduler.schedule(action) return Observable(subscribe) + + +__all__ = ["_throw"] diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index 9a43b6a8b..1f348e1ae 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -1,28 +1,30 @@ -from rx.core import Observable +from typing import Optional, Tuple, Any, List +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import NotSet -def _with_latest_from(parent: Observable, *sources: Observable) -> Observable: +def _with_latest_from(parent: Observable[Any], *sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: NO_VALUE = NotSet() - def subscribe(observer, scheduler=None): - def subscribe_all(parent, *children): + def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + def subscribe_all(parent: Observable[Any], *children: Observable[Any]) -> List[SingleAssignmentDisposable]: values = [NO_VALUE for _ in children] - def subscribe_child(i, child): + def subscribe_child(i: int, child: Observable[Any]): subscription = SingleAssignmentDisposable() - def on_next(value): + 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): + def on_next(value: Any) -> None: with parent.lock: if NO_VALUE not in values: result = (value,) + tuple(values) @@ -34,5 +36,10 @@ def on_next(value): children_subscription = [subscribe_child(i, child) for i, child in enumerate(children)] return [parent_subscription] + children_subscription + return CompositeDisposable(subscribe_all(parent, *sources)) + return Observable(subscribe) + + +__all__ = ["_with_latest_from"] diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index febbc604e..30e8ae5b2 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -1,33 +1,38 @@ -from typing import Any, Callable, Optional +from typing import Any, Callable, List, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe +_T = TypeVar("_T") + def _buffer(boundaries: Observable) -> Callable[[Observable], Observable]: return pipe( - ops.window(boundaries), - ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) - ) + ops.window(boundaries), ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) + ) -def _buffer_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observable], Observable]: +def _buffer_when( + closing_mapper: Callable[[], Observable] +) -> Callable[[Observable], Observable]: return pipe( ops.window_when(closing_mapper), - ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) - ) + ops.flat_map(pipe(ops.to_iterable(), ops.map(list))), + ) -def _buffer_toggle(openings: Observable, - closing_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: +def _buffer_toggle( + openings: Observable, closing_mapper: Callable[[Any], Observable] +) -> Callable[[Observable], Observable]: return pipe( ops.window_toggle(openings, closing_mapper), - ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) - ) + ops.flat_map(pipe(ops.to_iterable(), ops.map(list))), + ) -def _buffer_with_count(count: int, skip: Optional[int] = None) -> Callable[[Observable], Observable]: +def _buffer_with_count( + count: int, skip: Optional[int] = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on element count information. @@ -46,17 +51,22 @@ def _buffer_with_count(count: int, skip: Optional[int] = None) -> Callable[[Obse observable sequence of buffers. """ - def buffer_with_count(source: Observable) -> Observable: + def buffer_with_count(source: Observable[_T]) -> Observable[List[_T]]: nonlocal skip if skip is None: skip = count - def mapper(value): + def mapper(value: _T) -> List[_T]: return value.pipe(ops.to_iterable(), ops.map(list)) - def predicate(value): + def predicate(value: List[_T]) -> bool: return len(value) > 0 - return source.pipe(ops.window_with_count(count, skip), ops.flat_map(mapper), ops.filter(predicate)) + return source.pipe( + ops.window_with_count(count, skip), + ops.flat_map(mapper), + ops.filter(predicate), + ) + return buffer_with_count diff --git a/rx/core/operators/combinelatest.py b/rx/core/operators/combinelatest.py index 2937353a5..b4662fdcf 100644 --- a/rx/core/operators/combinelatest.py +++ b/rx/core/operators/combinelatest.py @@ -1,11 +1,13 @@ -from typing import Any, Callable, Iterable, List, Union +from typing import Any, Callable import rx -from rx.core import Observable, typing +from rx.core import Observable -def _combine_latest(*others: Observable) -> Callable[[Observable], Observable]: - def combine_latest(source: Observable) -> Observable: +def _combine_latest( + *others: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Any]]: + def combine_latest(source: Observable[Any]) -> Observable[Any]: """Merges the specified observable sequences into one observable sequence by creating a tuple whenever any of the observable sequences produces an element. @@ -21,4 +23,8 @@ def combine_latest(source: Observable) -> Observable: sources = (source,) + others return rx.combine_latest(*sources) + return combine_latest + + +__all__ = ["_combine_latest"] diff --git a/rx/core/operators/concat.py b/rx/core/operators/concat.py index 6c9eb5d63..45f3ed7d5 100644 --- a/rx/core/operators/concat.py +++ b/rx/core/operators/concat.py @@ -1,11 +1,13 @@ -from typing import Callable +from typing import Callable, TypeVar import rx from rx.core import Observable +_T = TypeVar("_T") -def _concat(*sources: Observable) -> Callable[[Observable], Observable]: - def concat(source: Observable) -> Observable: + +def _concat(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: + def concat(source: Observable[_T]) -> Observable[_T]: """Concatenates all the observable sequences. Examples: @@ -17,4 +19,8 @@ def concat(source: Observable) -> Observable: each given sequence, in sequential order. """ return rx.concat(source, *sources) + return concat + + +__all__ = ["_concat"] diff --git a/rx/core/operators/contains.py b/rx/core/operators/contains.py index 9d5bdcc38..540dd1993 100644 --- a/rx/core/operators/contains.py +++ b/rx/core/operators/contains.py @@ -1,17 +1,22 @@ -from typing import Any, Callable, Optional +from typing import TypeVar, Callable, Optional from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Comparer from rx.internal.basic import default_comparer +_T = TypeVar("_T") -def _contains(value: Any, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: + +def _contains( + value: _T, comparer: Optional[Comparer[_T]] = None +) -> Callable[[Observable[_T]], Observable[bool]]: comparer_ = comparer or default_comparer filtering = ops.filter(lambda v: comparer_(v, value)) something = ops.some() return pipe(filtering, something) + + +__all__ = ["_contains"] diff --git a/rx/core/operators/count.py b/rx/core/operators/count.py index e59825c71..29cb45e12 100644 --- a/rx/core/operators/count.py +++ b/rx/core/operators/count.py @@ -1,11 +1,15 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Predicate +_T = TypeVar("_T") -def _count(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: + +def _count( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[int]]: if predicate: filtering = ops.filter(predicate) @@ -13,3 +17,6 @@ def _count(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obse counter = ops.reduce(lambda n, _: n + 1, seed=0) return pipe(counter) + + +__all__ = ["_count"] diff --git a/rx/core/operators/window.py b/rx/core/operators/window.py index 125b476df..901b5c49c 100644 --- a/rx/core/operators/window.py +++ b/rx/core/operators/window.py @@ -1,21 +1,27 @@ import logging -from typing import Any, Callable +from typing import Any, Callable, Optional, TypeVar from rx import empty from rx import operators as ops -from rx.core import Observable -from rx.disposable import (CompositeDisposable, RefCountDisposable, - SerialDisposable, SingleAssignmentDisposable) +from rx.core import Observable, abc +from rx.disposable import ( + CompositeDisposable, + RefCountDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.internal import noop from rx.internal.utils import add_ref from rx.subject import Subject log = logging.getLogger("Rx") +_T = TypeVar("_T") -def _window_toggle(openings: Observable, - closing_mapper: Callable[[Any], Observable] - ) -> Callable[[Observable], Observable]: + +def _window_toggle( + openings: Observable, closing_mapper: Callable[[Any], Observable] +) -> Callable[[Observable], Observable]: """Projects each element of an observable sequence into zero or more windows. @@ -25,8 +31,8 @@ def _window_toggle(openings: Observable, Returns: An observable sequence of windows. """ - def window_toggle(source: Observable) -> Observable: + def window_toggle(source: Observable) -> Observable: def mapper(args): _, window = args return window @@ -36,13 +42,16 @@ def mapper(args): source, closing_mapper, lambda _: empty(), - ), + ), ops.map(mapper), - ) + ) + return window_toggle -def _window(boundaries: Observable) -> Callable[[Observable], Observable]: +def _window( + boundaries: Observable[Any], +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -52,41 +61,52 @@ def _window(boundaries: Observable) -> Callable[[Observable], Observable]: Returns: An observable sequence of windows. """ - def window(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - window_subject = Subject() + def window(source: Observable[_T]) -> Observable[Observable[_T]]: + def subscribe( + observer: abc.ObserverBase[Observable[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + window_subject: Subject[_T] = Subject() d = CompositeDisposable() r = RefCountDisposable(d) observer.on_next(add_ref(window_subject, r)) - def on_next_window(x): + def on_next_window(x: _T) -> None: window_subject.on_next(x) - def on_error(err): + def on_error(err: Exception) -> None: window_subject.on_error(err) observer.on_error(err) - def on_completed(): + def on_completed() -> None: window_subject.on_completed() observer.on_completed() d.add(source.subscribe_(on_next_window, on_error, on_completed, scheduler)) - def on_next_observer(w): + def on_next_observer(w: Observable[_T]): nonlocal window_subject window_subject.on_completed() window_subject = Subject() observer.on_next(add_ref(window_subject, r)) - d.add(boundaries.subscribe_(on_next_observer, on_error, on_completed, scheduler)) + d.add( + boundaries.subscribe_( + on_next_observer, on_error, on_completed, scheduler + ) + ) return r + return Observable(subscribe) + return window -def _window_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observable], Observable]: +def _window_when( + closing_mapper: Callable[[], Observable[_T]] +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -96,24 +116,27 @@ def _window_when(closing_mapper: Callable[[], Observable]) -> Callable[[Observab Returns: An observable sequence of windows. """ - def window_when(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def window_when(source: Observable[_T]) -> Observable[Observable[_T]]: + def subscribe( + observer: abc.ObserverBase[Observable[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ): m = SerialDisposable() d = CompositeDisposable(m) r = RefCountDisposable(d) - window = Subject() + window: Subject[_T] = Subject() observer.on_next(add_ref(window, r)) - def on_next(value): + def on_next(value: _T) -> None: window.on_next(value) - def on_error(error): + def on_error(error: Exception) -> None: window.on_error(error) observer.on_error(error) - def on_completed(): + def on_completed() -> None: window.on_completed() observer.on_completed() @@ -135,9 +158,13 @@ def on_completed(): m1 = SingleAssignmentDisposable() m.disposable = m1 - m1.disposable = window_close.pipe(ops.take(1)).subscribe_(noop, on_error, on_completed, scheduler) + m1.disposable = window_close.pipe(ops.take(1)).subscribe_( + noop, on_error, on_completed, scheduler + ) create_window_on_completed() return r + return Observable(subscribe) + return window_when diff --git a/rx/internal/utils.py b/rx/internal/utils.py index 9c5c0aaa4..70fb963ad 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -5,9 +5,10 @@ from rx.core import abc from rx.disposable import CompositeDisposable +from rx.disposable.refcountdisposable import RefCountDisposable -def add_ref(xs: abc.ObservableBase[Any], r): +def add_ref(xs: abc.ObservableBase[Any], r: RefCountDisposable): from rx.core import Observable def subscribe( diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 41bff3f24..ffe5e05cb 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1,7 +1,18 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin from asyncio import Future -from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar, Union, cast +from typing import ( + Any, + Callable, + Iterable, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, + cast, +) from rx.core import ( ConnectableObservable, @@ -226,7 +237,7 @@ def buffer_toggle( def buffer_with_count( count: int, skip: Optional[int] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on element count information. @@ -260,7 +271,7 @@ def buffer_with_time( timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on timing information. @@ -297,8 +308,10 @@ def buffer_with_time( def buffer_with_time_or_count( - timespan, count, scheduler=None -) -> Callable[[Observable], Observable]: + timespan: typing.RelativeTime, + count: int, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Projects each element of an observable sequence into a buffer that is completed when either it's full or a given amount of time has elapsed. @@ -367,7 +380,9 @@ def catch( return _catch(handler) -def combine_latest(*others: Observable) -> Callable[[Observable], Observable]: +def combine_latest( + *others: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Any]]: """Merges the specified observable sequences into one observable sequence by creating a tuple whenever any of the observable sequences produces an element. @@ -394,7 +409,7 @@ def combine_latest(*others: Observable) -> Callable[[Observable], Observable]: return _combine_latest(*others) -def concat(*sources: Observable) -> Callable[[Observable], Observable]: +def concat(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Concatenates all the observable sequences. .. marble:: @@ -419,8 +434,8 @@ def concat(*sources: Observable) -> Callable[[Observable], Observable]: def contains( - value: Any, comparer: Optional[typing.Comparer] = None -) -> Callable[[Observable], Observable]: + value: _T, comparer: Optional[typing.Comparer[_T]] = None +) -> Callable[[Observable[_T]], Observable[bool]]: """Determines whether an observable sequence contains a specified element with an optional equality comparer. @@ -451,8 +466,8 @@ def contains( def count( - predicate: Optional[typing.Predicate] = None, -) -> Callable[[Observable], Observable]: + predicate: Optional[typing.Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[int]]: """Returns an observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items. @@ -3482,7 +3497,7 @@ def to_future( return _to_future(future_ctor) -def to_iterable() -> Callable[[Observable], Observable]: +def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: """Creates an iterable from an observable sequence. There is also an alias called ``to_list``. @@ -3519,7 +3534,7 @@ def to_marbles( return _to_marbles(scheduler=scheduler, timespan=timespan) -def to_set() -> Callable[[Observable], Observable]: +def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: """Converts the observable sequence to a set. Returns: @@ -3532,7 +3547,7 @@ def to_set() -> Callable[[Observable], Observable]: return _to_set() -def while_do(condition: Predicate) -> Callable[[Observable], Observable]: +def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a while loop. @@ -3550,7 +3565,9 @@ def while_do(condition: Predicate) -> Callable[[Observable], Observable]: return _while_do(condition) -def window(boundaries: Observable) -> Callable[[Observable], Observable]: +def window( + boundaries: Observable[Any], +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. From 2fde031400327b27d3a2c172582ced66b1f11a64 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 14:56:59 +0100 Subject: [PATCH 008/103] Type hint fixes --- rx/core/__init__.py | 2 + rx/core/operators/dematerialize.py | 26 ++++++++--- rx/core/operators/max.py | 16 ++++--- rx/core/operators/maxby.py | 21 ++++++--- rx/core/operators/min.py | 18 +++++--- rx/core/operators/minby.py | 64 ++++++++++++++++----------- rx/core/operators/retry.py | 14 ++++-- rx/core/operators/sample.py | 53 ++++++++++++++-------- rx/core/operators/sequenceequal.py | 45 ++++++++++++------- rx/core/operators/single.py | 11 ++++- rx/core/operators/singleordefault.py | 26 +++++++---- rx/core/operators/skip.py | 26 ++++++++--- rx/core/operators/skiplastwithtime.py | 30 +++++++++---- rx/core/operators/sum.py | 7 +-- rx/core/operators/take.py | 26 ++++++++--- rx/core/operators/timestamp.py | 10 +++-- rx/core/operators/toset.py | 28 ++++++++---- rx/core/operators/whiledo.py | 20 ++++++--- rx/core/operators/windowwithcount.py | 32 +++++++++----- rx/core/operators/zip.py | 37 ++++++++++++---- rx/core/run.py | 8 ++-- rx/core/typing.py | 2 +- rx/internal/basic.py | 5 ++- rx/internal/utils.py | 9 +++- rx/operators/__init__.py | 30 ++++++++----- 25 files changed, 380 insertions(+), 186 deletions(-) diff --git a/rx/core/__init__.py b/rx/core/__init__.py index e2dfc1dff..140260298 100644 --- a/rx/core/__init__.py +++ b/rx/core/__init__.py @@ -2,6 +2,7 @@ from .observable import ConnectableObservable, GroupedObservable, Observable from .observer import Observer from .pipe import pipe +from .notification import Notification __all__ = [ "abc", @@ -10,4 +11,5 @@ "ConnectableObservable", "GroupedObservable", "Observer", + "Notification", ] diff --git a/rx/core/operators/dematerialize.py b/rx/core/operators/dematerialize.py index 1117df143..8b4784080 100644 --- a/rx/core/operators/dematerialize.py +++ b/rx/core/operators/dematerialize.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, Notification, abc +_T = TypeVar("_T") -def _dematerialize() -> Callable[[Observable], Observable]: - def dematerialize(source: Observable) -> Observable: + +def _dematerialize() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: + def dematerialize(source: Observable[Notification[_T]]) -> Observable[_T]: """Partially applied dematerialize operator. Dematerializes the explicit notification values of an @@ -15,10 +17,20 @@ def dematerialize(source: Observable) -> Observable: corresponding to the source sequence's notification values. """ - def subscribe(observer, scheduler=None): - def on_next(value): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): + def on_next(value: Notification[_T]) -> None: return value.accept(observer) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return dematerialize + + +__all__ = ["_dematerialize"] diff --git a/rx/core/operators/max.py b/rx/core/operators/max.py index 28b540774..1a7898355 100644 --- a/rx/core/operators/max.py +++ b/rx/core/operators/max.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe @@ -7,8 +7,12 @@ from .min import first_only +_T = TypeVar("_T") -def _max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: + +def _max( + comparer: Optional[Comparer[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the maximum value in an observable sequence according to the specified comparer. @@ -25,7 +29,7 @@ def _max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observab maximum element in the source sequence. """ - return pipe( - ops.max_by(identity, comparer), - ops.map(first_only) - ) + return pipe(ops.max_by(identity, comparer), ops.map(first_only)) + + +__all__ = ["_max"] diff --git a/rx/core/operators/maxby.py b/rx/core/operators/maxby.py index e066cfa98..ece95b3a6 100644 --- a/rx/core/operators/maxby.py +++ b/rx/core/operators/maxby.py @@ -1,19 +1,22 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, List -from rx.core import Observable -from rx.core.typing import Mapper, SubComparer +from rx.core import Observable, typing from rx.internal.basic import default_sub_comparer from .minby import extrema_by +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") -def _max_by(key_mapper: Mapper, - comparer: Optional[SubComparer] = None - ) -> Callable[[Observable], Observable]: + +def _max_by( + key_mapper: typing.Mapper[_T, _TKey], + comparer: Optional[typing.SubComparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[List[_T]]]: cmp = comparer or default_sub_comparer - def max_by(source: Observable) -> Observable: + def max_by(source: Observable[_T]) -> Observable[List[_T]]: """Partially applied max_by operator. Returns the elements in an observable sequence with the maximum @@ -30,4 +33,8 @@ def max_by(source: Observable) -> Observable: elements that have a maximum key value. """ return extrema_by(source, key_mapper, cmp) + return max_by + + +__all__ = ["_max_by"] diff --git a/rx/core/operators/min.py b/rx/core/operators/min.py index b1e296263..bb83875e3 100644 --- a/rx/core/operators/min.py +++ b/rx/core/operators/min.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Optional, Sequence +from typing import Callable, Optional, Sequence, TypeVar from rx import operators as ops from rx.core import Observable, pipe @@ -6,15 +6,19 @@ from rx.internal.basic import identity from rx.internal.exceptions import SequenceContainsNoElementsError +_T = TypeVar("_T") -def first_only(x: Sequence) -> Any: + +def first_only(x: Sequence[_T]) -> _T: if not x: raise SequenceContainsNoElementsError() return x[0] -def _min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def _min( + comparer: Optional[Comparer[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """The `min` operator. Returns the minimum element in an observable sequence according to @@ -32,7 +36,7 @@ def _min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observab with the minimum element in the source sequence. """ - return pipe( - ops.min_by(identity, comparer), - ops.map(first_only) - ) + return pipe(ops.min_by(identity, comparer), ops.map(first_only)) + + +__all__ = ["_min"] diff --git a/rx/core/operators/minby.py b/rx/core/operators/minby.py index 84b22918c..247a8d33f 100644 --- a/rx/core/operators/minby.py +++ b/rx/core/operators/minby.py @@ -1,21 +1,27 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, cast, List -from rx.core import Observable -from rx.core.typing import Mapper, SubComparer +from rx.core import Observable, typing, abc from rx.internal.basic import default_sub_comparer - -def extrema_by(source: Observable, - key_mapper: Mapper, - comparer: SubComparer - ) -> Observable: - - def subscribe(observer, scheduler=None): - has_value = [False] - last_key = [None] - items = [] - - def on_next(x): +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") + + +def extrema_by( + source: Observable[_T], + key_mapper: typing.Mapper[_T, _TKey], + comparer: typing.SubComparer[_TKey], +) -> Observable[List[_T]]: + def subscribe( + observer: abc.ObserverBase[List[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + has_value = False + last_key: _TKey = cast(_TKey, None) + items: List[_T] = [] + + def on_next(x: _T) -> None: + nonlocal has_value, last_key try: key = key_mapper(x) except Exception as ex: @@ -24,18 +30,18 @@ def on_next(x): comparison = 0 - if not has_value[0]: - has_value[0] = True - last_key[0] = key + if not has_value: + has_value = True + last_key = key else: try: - comparison = comparer(key, last_key[0]) + comparison = comparer(key, last_key) except Exception as ex1: observer.on_error(ex1) return if comparison > 0: - last_key[0] = key + last_key = key items[:] = [] if comparison >= 0: @@ -46,12 +52,14 @@ def on_completed(): observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return Observable(subscribe) -def _min_by(key_mapper: Mapper, - comparer: Optional[SubComparer] = None - ) -> Callable[[Observable], Observable]: +def _min_by( + key_mapper: typing.Mapper[_T, _TKey], + comparer: Optional[typing.SubComparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """The `min_by` operator. Returns the elements in an observable sequence with the minimum key @@ -70,8 +78,12 @@ def _min_by(key_mapper: Mapper, elements that have a minimum key value. """ - cmp: SubComparer = comparer or default_sub_comparer + cmp = comparer or default_sub_comparer + + def min_by(source: Observable[_T]) -> Observable[List[_T]]: + return extrema_by(source, key_mapper, lambda x, y: -cmp(x, y)) - def min_by(source: Observable) -> Observable: - return extrema_by(source, key_mapper, lambda x, y: - cmp(x, y)) return min_by + + +__all__ = ["_min_by"] diff --git a/rx/core/operators/retry.py b/rx/core/operators/retry.py index 9ce77538d..d4a4b95dd 100644 --- a/rx/core/operators/retry.py +++ b/rx/core/operators/retry.py @@ -1,11 +1,15 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar import rx from rx.core import Observable from rx.internal.utils import infinite +_T = TypeVar("_T") -def _retry(retry_count: Optional[int] = None) -> Callable[[Observable], Observable]: + +def _retry( + retry_count: Optional[int] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the source observable sequence the specified number of times or until it successfully terminates. If the retry count is not specified, it retries indefinitely. @@ -28,6 +32,10 @@ def _retry(retry_count: Optional[int] = None) -> Callable[[Observable], Observab else: gen = range(retry_count) - def retry(source: Observable) -> Observable: + def retry(source: Observable[_T]) -> Observable[_T]: return rx.catch_with_iterable(source for _ in gen) + return retry + + +__all__ = ["_retry"] diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index d91a3c4a4..2ac74cfeb 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -1,43 +1,55 @@ -from typing import Callable, Optional, Union +from typing import Callable, Optional, TypeVar, Union, Any, cast import rx from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable +_T = TypeVar("_T") -def sample_observable(source: Observable, sampler: Observable) -> Observable: - def subscribe(observer, scheduler=None): - at_end = [None] - has_value = [None] - value = [None] - def sample_subscribe(x=None): - if has_value[0]: - has_value[0] = False - observer.on_next(value[0]) +def sample_observable( + source: Observable[_T], sampler: Observable[Any] +) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ): + at_end = False + has_value = False + value: _T = cast(_T, None) - if at_end[0]: + def sample_subscribe(_: Any = None) -> None: + nonlocal has_value + if has_value: + has_value = False + observer.on_next(value) + + if at_end: observer.on_completed() - def on_next(new_value): - has_value[0] = True - value[0] = new_value + def on_next(new_value: _T): + nonlocal has_value, value + has_value = True + value = new_value def on_completed(): - at_end[0] = True + nonlocal at_end + at_end = True return CompositeDisposable( source.subscribe_(on_next, observer.on_error, on_completed, scheduler), - sampler.subscribe_(sample_subscribe, observer.on_error, sample_subscribe, scheduler), + sampler.subscribe_( + sample_subscribe, observer.on_error, sample_subscribe, scheduler + ), ) return Observable(subscribe) def _sample( - sampler: Union[typing.RelativeTime, Observable], scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def sample(source: Observable) -> Observable: + sampler: Union[typing.RelativeTime, Observable[Any]], + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def sample(source: Observable[_T]) -> Observable[_T]: """Samples the observable sequence at each interval. Examples: @@ -56,3 +68,6 @@ def sample(source: Observable) -> Observable: return sample_observable(source, rx.interval(sampler, scheduler=scheduler)) return sample + + +__all__ = ["_sample"] diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index c1f8fd7a6..ec5067867 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -1,20 +1,21 @@ -import collections -from typing import Any, Callable, Optional +from typing import Callable, List, Optional, TypeVar, Iterable import rx -from rx.core import Observable -from rx.core.typing import Comparer +from rx.core import Observable, typing, abc from rx.disposable import CompositeDisposable from rx.internal import default_comparer +_T = TypeVar("_T") -def _sequence_equal(second: Observable, comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: + +def _sequence_equal( + second: Observable[_T], comparer: Optional[typing.Comparer[_T]] = None +) -> Callable[[Observable[_T]], Observable[bool]]: comparer = comparer or default_comparer - if isinstance(second, collections.abc.Iterable): + if isinstance(second, Iterable): second = rx.from_iterable(second) - def sequence_equal(source: Observable) -> Observable: + def sequence_equal(source: Observable[_T]) -> Observable[bool]: """Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer. @@ -35,13 +36,16 @@ def sequence_equal(source: Observable) -> Observable: """ first = source - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[bool], + scheduler: Optional[abc.SchedulerBase] = None, + ): donel = [False] doner = [False] - ql = [] - qr = [] + ql: List[_T] = [] + qr: List[_T] = [] - def on_next1(x): + def on_next1(x: _T) -> None: if len(qr) > 0: v = qr.pop(0) try: @@ -60,7 +64,7 @@ def on_next1(x): else: ql.append(x) - def on_completed1(): + def on_completed1() -> None: donel[0] = True if not ql: if qr: @@ -70,7 +74,7 @@ def on_completed1(): observer.on_next(True) observer.on_completed() - def on_next2(x): + def on_next2(x: _T): if len(ql) > 0: v = ql.pop(0) try: @@ -99,8 +103,17 @@ def on_completed2(): observer.on_next(True) observer.on_completed() - subscription1 = first.subscribe_(on_next1, observer.on_error, on_completed1, scheduler) - subscription2 = second.subscribe_(on_next2, observer.on_error, on_completed2, scheduler) + subscription1 = first.subscribe_( + on_next1, observer.on_error, on_completed1, scheduler + ) + subscription2 = second.subscribe_( + on_next2, observer.on_error, on_completed2, scheduler + ) return CompositeDisposable(subscription1, subscription2) + return Observable(subscribe) + return sequence_equal + + +__all__ = ["_sequence_equal"] diff --git a/rx/core/operators/single.py b/rx/core/operators/single.py index 780e65e1d..dd71aef83 100644 --- a/rx/core/operators/single.py +++ b/rx/core/operators/single.py @@ -1,11 +1,15 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Predicate +_T = TypeVar("_T") -def _single(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: + +def _single( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that satisfies the condition in the optional predicate, and reports an exception if there is not exactly one element in the observable sequence. @@ -27,3 +31,6 @@ def _single(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obs return pipe(ops.filter(predicate), ops.single()) else: return ops.single_or_default_async(False) + + +__all__ = ["_single"] diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index 1e9306bff..a8efa349e 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -1,20 +1,25 @@ -from typing import Callable, Optional, Any +from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, pipe +from rx.core import Observable, pipe, abc from rx.core.typing import Predicate from rx.internal.exceptions import SequenceContainsNoElementsError +_T = TypeVar("_T") + def _single_or_default_async( - has_default: bool = False, default_value: Any = None -) -> Callable[[Observable], Observable]: - def single_or_default_async(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + has_default: bool = False, default_value: _T = None +) -> Callable[[Observable[_T]], Observable[_T]]: + def single_or_default_async(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): value = [default_value] seen_value = [False] - def on_next(x): + def on_next(x: _T): if seen_value[0]: observer.on_error( Exception("Sequence contains more than one element") @@ -40,8 +45,8 @@ def on_completed(): def _single_or_default( - predicate: Optional[Predicate] = None, default_value: Any = None -) -> Callable[[Observable], Observable]: + predicate: Optional[Predicate[_T]] = None, default_value: _T = None +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that matches the predicate, or a default value if no such element exists this method reports an exception if there is more than one element in the @@ -69,3 +74,6 @@ def _single_or_default( return pipe(ops.filter(predicate), ops.single_or_default(None, default_value)) else: return _single_or_default_async(True, default_value) + + +__all__ = ["_single_or_default", "_single_or_default_async"] diff --git a/rx/core/operators/skip.py b/rx/core/operators/skip.py index 38fe6baf4..c0649534f 100644 --- a/rx/core/operators/skip.py +++ b/rx/core/operators/skip.py @@ -1,14 +1,16 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc from rx.internal import ArgumentOutOfRangeException +_T = TypeVar("_T") -def _skip(count: int) -> Callable[[Observable], Observable]: + +def _skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: if count < 0: raise ArgumentOutOfRangeException() - def skip(source: Observable) -> Observable: + def skip(source: Observable[_T]) -> Observable[_T]: """The skip operator. Bypasses a specified number of elements in an observable sequence @@ -22,10 +24,13 @@ def skip(source: Observable) -> Observable: after the specified index in the input sequence. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): remaining = count - def on_next(value): + def on_next(value: _T) -> None: nonlocal remaining if remaining <= 0: @@ -33,6 +38,13 @@ def on_next(value): else: remaining -= 1 - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return skip + + +__all__ = ["_skip"] diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index a154a84e7..21827a6b2 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -1,12 +1,14 @@ -from typing import Callable, Optional +from typing import Callable, Dict, List, Optional, TypeVar, Any from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") + def _skip_last_with_time( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements for the specified duration from the end of the observable source sequence. @@ -29,29 +31,39 @@ def _skip_last_with_time( specified duration from the end of the source sequence. """ - def skip_last_with_time(source: Observable) -> Observable: - def subscribe(observer, scheduler_=None): + def skip_last_with_time(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: nonlocal duration - _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() + _scheduler: abc.SchedulerBase = ( + scheduler or scheduler_ or TimeoutScheduler.singleton() + ) duration = _scheduler.to_timedelta(duration) - q = [] + q: List[Dict[str, Any]] = [] - def on_next(x): + def on_next(x: _T) -> None: now = _scheduler.now q.append({"interval": now, "value": x}) while q and now - q[0]["interval"] >= duration: observer.on_next(q.pop(0)["value"]) - def on_completed(): + def on_completed() -> None: now = _scheduler.now while q and now - q[0]["interval"] >= duration: observer.on_next(q.pop(0)["value"]) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler_) + return source.subscribe_( + on_next, observer.on_error, on_completed, _scheduler + ) return Observable(subscribe) return skip_last_with_time + + +__all__ = ["_skip_last_with_time"] diff --git a/rx/core/operators/sum.py b/rx/core/operators/sum.py index 84059211f..058b3f0b9 100644 --- a/rx/core/operators/sum.py +++ b/rx/core/operators/sum.py @@ -4,11 +4,12 @@ from rx.core import Observable, pipe from rx.core.typing import Mapper -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") +_T = TypeVar("_T") -def _sum(key_mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def _sum( + key_mapper: Optional[Mapper[_T, int]] = None +) -> Callable[[Observable[_T]], Observable[int]]: if key_mapper: return pipe(ops.map(key_mapper), ops.sum()) diff --git a/rx/core/operators/take.py b/rx/core/operators/take.py index 6d8cd8391..5c8d87998 100644 --- a/rx/core/operators/take.py +++ b/rx/core/operators/take.py @@ -1,15 +1,17 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar from rx import empty -from rx.core import Observable +from rx.core import Observable, abc from rx.internal import ArgumentOutOfRangeException +_T = TypeVar("_T") -def _take(count: int) -> Callable[[Observable], Observable]: + +def _take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: if count < 0: raise ArgumentOutOfRangeException() - def take(source: Observable) -> Observable: + def take(source: Observable[_T]) -> Observable[_T]: """Returns a specified number of contiguous elements from the start of an observable sequence. @@ -25,10 +27,13 @@ def take(source: Observable) -> Observable: if not count: return empty() - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): remaining = count - def on_next(value): + def on_next(value: _T) -> None: nonlocal remaining if remaining > 0: @@ -37,6 +42,13 @@ def on_next(value): if not remaining: observer.on_completed() - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return take + + +__all__ = ["_take"] diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index d388fc9c8..aecb43c83 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -11,8 +11,10 @@ class Timestamp(NamedTuple): timestamp: datetime -def _timestamp(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: - def timestamp(source: Observable) -> Observable: +def _timestamp( + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[Any]], Observable[Timestamp]]: + def timestamp(source: Observable[Any]) -> Observable[Timestamp]: """Records the timestamp for each value in an observable sequence. Examples: @@ -30,7 +32,9 @@ def timestamp(source: Observable) -> Observable: def factory(scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - mapper = operators.map(lambda value: Timestamp(value=value, timestamp=_scheduler.now)) + mapper = operators.map( + lambda value: Timestamp(value=value, timestamp=_scheduler.now) + ) return source.pipe(mapper) diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index 57d6d973e..e5a6f73f4 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -1,25 +1,35 @@ -from typing import Callable +from typing import Callable, TypeVar, Set, Optional -from rx.core import Observable +from rx.core import Observable, abc +_T = TypeVar("_T") -def _to_set() -> Callable[[Observable], Observable]: + +def _to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: """Converts the observable sequence to a set. Returns an observable sequence with a single value of a set containing the values from the observable sequence. """ - def to_set(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - s = set() + def to_set(source: Observable[_T]) -> Observable[Set[_T]]: + def subscribe( + observer: abc.ObserverBase[Set[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + s = set[_T]() - def on_completed(): + def on_completed() -> None: nonlocal s observer.on_next(s) - s = set() + s = set[_T]() observer.on_completed() return source.subscribe_(s.add, observer.on_error, on_completed, scheduler) + return Observable(subscribe) - return to_set \ No newline at end of file + + return to_set + + +__all__ = ["_to_set"] diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index 86e69a4d6..d3b6f7599 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -1,15 +1,17 @@ import itertools from asyncio import Future -from typing import Callable, Union, cast +from typing import Callable, Union, TypeVar import rx from rx.core import Observable from rx.core.typing import Predicate -from rx.internal.utils import infinite, is_future +from rx.internal.utils import infinite +_T = TypeVar("_T") -def _while_do(condition: Predicate) -> Callable[[Observable], Observable]: - def while_do(source: Union[Observable, Future]) -> Observable: + +def _while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: + def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: """Repeats source as long as condition holds emulating a while loop. @@ -21,10 +23,14 @@ def while_do(source: Union[Observable, Future]) -> Observable: An observable sequence which is repeated as long as the condition holds. """ - if is_future(source): - obs = rx.from_future(cast(Future, source)) + if isinstance(source, Future): + obs = rx.from_future(source) else: - obs = cast(Observable, source) + obs = source it = itertools.takewhile(condition, (obs for _ in infinite())) return rx.concat_with_iterable(it) + return while_do + + +__all__ = ["_while_do"] diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index 8ca515871..eec0b5cd0 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -1,7 +1,7 @@ import logging -from typing import Callable, Optional +from typing import Callable, List, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import RefCountDisposable, SingleAssignmentDisposable from rx.internal.exceptions import ArgumentOutOfRangeException from rx.internal.utils import add_ref @@ -9,8 +9,12 @@ log = logging.getLogger("Rx") +_T = TypeVar("_T") -def _window_with_count(count: int, skip: Optional[int] = None) -> Callable[[Observable], Observable]: + +def _window_with_count( + count: int, skip: Optional[int] = None +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows which are produced based on element count information. @@ -37,21 +41,24 @@ def _window_with_count(count: int, skip: Optional[int] = None) -> Callable[[Obse if skip <= 0: raise ArgumentOutOfRangeException() - def window_with_count(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def window_with_count(source: Observable[_T]) -> Observable[Observable[_T]]: + def subscribe( + observer: abc.ObserverBase[Observable[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ): m = SingleAssignmentDisposable() refCountDisposable = RefCountDisposable(m) n = [0] - q = [] + q: List[Subject[_T]] = [] def create_window(): - s = Subject() + s = Subject[_T]() q.append(s) observer.on_next(add_ref(s, refCountDisposable)) create_window() - def on_next(x): + def on_next(x: _T) -> None: for item in q: item.on_next(x) @@ -64,17 +71,22 @@ def on_next(x): if (n[0] % skip) == 0: create_window() - def on_error(exception): + def on_error(exception: Exception) -> None: while q: q.pop(0).on_error(exception) observer.on_error(exception) - def on_completed(): + def on_completed() -> None: while q: q.pop(0).on_completed() observer.on_completed() m.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler) return refCountDisposable + return Observable(subscribe) + return window_with_count + + +__all__ = ["_window_with_count"] diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index 3f58b16cc..20eca8093 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -1,12 +1,17 @@ -from typing import Callable, Iterable +from typing import Callable, Iterable, Any, Optional, Tuple, TypeVar import rx -from rx.core import Observable +from rx.core import Observable, abc + +_T = TypeVar("_T") +_TOther = TypeVar("_TOther") # pylint: disable=redefined-builtin -def _zip(*args: Observable) -> Callable[[Observable], Observable]: - def zip(source: Observable) -> Observable: +def _zip( + *args: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Tuple[Any, ...]]]: + def zip(source: 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 @@ -23,10 +28,14 @@ def zip(source: Observable) -> Observable: elements of the sources as a tuple. """ return rx.zip(source, *args) + return zip -def _zip_with_iterable(seq: Iterable) -> Callable[[Observable], Observable]: - def zip_with_iterable(source: Observable) -> Observable: + +def _zip_with_iterable( + seq: Iterable[_TOther], +) -> Callable[[Observable[_T]], Observable[Tuple[_T, _TOther]]]: + def zip_with_iterable(source: Observable[_T]) -> Observable[Tuple[_T, _TOther]]: """Merges the specified observable sequence and list into one observable sequence by creating a tuple whenever all of the observable sequences have produced an element at a @@ -46,10 +55,13 @@ def zip_with_iterable(source: Observable) -> Observable: first = source second = iter(seq) - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Tuple[_T, _TOther]], + scheduler: Optional[abc.SchedulerBase] = None, + ): index = 0 - def on_next(left): + def on_next(left: _T) -> None: nonlocal index try: @@ -60,6 +72,13 @@ def on_next(left): result = (left, right) observer.on_next(result) - return first.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return first.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return zip_with_iterable + + +__all__ = ["_zip", "_zip_with_iterable"] diff --git a/rx/core/run.py b/rx/core/run.py index 0bd8249ec..6f33ae18b 100644 --- a/rx/core/run.py +++ b/rx/core/run.py @@ -1,5 +1,5 @@ import threading -from typing import Any, Optional +from typing import Any, Optional, TypeVar from rx.internal.exceptions import SequenceContainsNoElementsError from rx.scheduler import NewThreadScheduler @@ -8,8 +8,10 @@ scheduler = NewThreadScheduler() +_T = TypeVar("_T") -def run(source: Observable) -> Any: + +def run(source: Observable[_T]) -> _T: """Run source synchronously. Subscribes to the observable source. Then blocks and waits for the @@ -58,7 +60,7 @@ def on_completed() -> None: while not done: latch.wait() - if exception and isinstance(exception, Exception): + if isinstance(exception, Exception): raise exception # pylint: disable=raising-bad-type if not has_result: diff --git a/rx/core/typing.py b/rx/core/typing.py index 65e0d4bb2..8e489d7c8 100644 --- a/rx/core/typing.py +++ b/rx/core/typing.py @@ -26,7 +26,7 @@ Predicate = Callable[[_T1], bool] PredicateIndexed = Callable[[_T1, int], bool] Comparer = Callable[[_T1, _T1], bool] -SubComparer = Callable[[_T1, _T2], int] +SubComparer = Callable[[_T1, _T1], int] Accumulator = Callable[[_TState, _T1], _TState] diff --git a/rx/internal/basic.py b/rx/internal/basic.py index 18791d8cb..4c42afc1d 100644 --- a/rx/internal/basic.py +++ b/rx/internal/basic.py @@ -1,6 +1,7 @@ from datetime import datetime -from typing import Any, NoReturn, Union +from typing import Any, NoReturn, Union, TypeVar +_T = TypeVar("_T") # Defaults def noop(*args: Any, **kw: Any): @@ -8,7 +9,7 @@ def noop(*args: Any, **kw: Any): pass -def identity(x: Any) -> Any: +def identity(x: _T) -> _T: """Returns argument x""" return x diff --git a/rx/internal/utils.py b/rx/internal/utils.py index 70fb963ad..e13dd70a9 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -1,14 +1,19 @@ from asyncio import Future from functools import update_wrapper from types import FunctionType -from typing import Any, Callable, Iterable, Optional, cast +from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, cast, TypeVar from rx.core import abc from rx.disposable import CompositeDisposable from rx.disposable.refcountdisposable import RefCountDisposable +if TYPE_CHECKING: + from rx.core import Observable + +_T = TypeVar("_T") + -def add_ref(xs: abc.ObservableBase[Any], r: RefCountDisposable): +def add_ref(xs: "Observable[_T]", r: RefCountDisposable) -> "Observable[_T]": from rx.core import Observable def subscribe( diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index ffe5e05cb..aa8bab82a 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1,6 +1,7 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin from asyncio import Future +from tkinter import NO from typing import ( Any, Callable, @@ -21,6 +22,7 @@ abc, pipe, typing, + Notification, ) from rx.core.typing import ( Accumulator, @@ -627,7 +629,7 @@ def delay_with_mapper( return _delay_with_mapper(subscription_delay, delay_duration_mapper) -def dematerialize() -> Callable[[Observable], Observable]: +def dematerialize() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: """Dematerialize operator. Dematerializes the explicit notification values of an @@ -1643,7 +1645,7 @@ def map_indexed( return _map_indexed(mapper_indexed) -def materialize() -> Callable[[Observable], Observable]: +def materialize() -> Callable[[Observable[_T]], Observable[Notification[_T]]]: """Materializes the implicit notifications of an observable sequence as explicit notification values. @@ -1657,7 +1659,9 @@ def materialize() -> Callable[[Observable], Observable]: return _materialize() -def max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def max( + comparer: Optional[Comparer[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the maximum value in an observable sequence according to the specified comparer. @@ -1686,8 +1690,8 @@ def max(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl def max_by( - key_mapper: Mapper, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: + key_mapper: Mapper[_T, _TKey], comparer: Optional[Comparer[_TKey]] = None +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """The max_by operator. Returns the elements in an observable sequence with the maximum @@ -1719,8 +1723,8 @@ def max_by( def merge( - *sources: Observable, max_concurrent: Optional[int] = None -) -> Callable[[Observable], Observable]: + *sources: Observable[Any], max_concurrent: Optional[int] = None +) -> Callable[[Observable[Any]], Observable[Any]]: """Merges an observable sequence of observable sequences into an observable sequence, limiting the number of concurrent subscriptions to inner sequences. Or merges two observable @@ -1777,7 +1781,9 @@ def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: return _merge_all() -def min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observable]: +def min( + comparer: Optional[Comparer[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """The `min` operator. Returns the minimum element in an observable sequence according to @@ -1808,8 +1814,8 @@ def min(comparer: Optional[Comparer] = None) -> Callable[[Observable], Observabl def min_by( - key_mapper: Mapper, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: + key_mapper: Mapper[_T, _TKey], comparer: Optional[Comparer[_TKey]] = None +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """The `min_by` operator. Returns the elements in an observable sequence with the minimum key @@ -2930,8 +2936,8 @@ def subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observa def sum( - key_mapper: Optional[Mapper[_T1, _T2]] = None -) -> Callable[[Observable[_T1]], Observable[_T2]]: + key_mapper: Optional[Mapper[_T, int]] = None +) -> Callable[[Observable[_T]], Observable[int]]: """Computes the sum of a sequence of values that are obtained by invoking an optional transform function on each element of the input sequence, else if not specified computes the sum on each item From 8563732cdd15f1c27dad814d2385a4951d82d9eb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 18:27:47 +0100 Subject: [PATCH 009/103] Fix tests for older Python --- rx/core/operators/timestamp.py | 5 ++++- rx/core/operators/toset.py | 4 ++-- rx/core/operators/windowwithcount.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index aecb43c83..58d66b56a 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -30,7 +30,7 @@ def timestamp(source: Observable[Any]) -> Observable[Timestamp]: An observable sequence with timestamp information on values. """ - def factory(scheduler_=None): + def factory(scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() mapper = operators.map( lambda value: Timestamp(value=value, timestamp=_scheduler.now) @@ -41,3 +41,6 @@ def factory(scheduler_=None): return defer(factory) return timestamp + + +__all__ = ["_timestamp"] diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index e5a6f73f4..375a943ed 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -17,12 +17,12 @@ def subscribe( observer: abc.ObserverBase[Set[_T]], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - s = set[_T]() + s: Set[_T] = set() def on_completed() -> None: nonlocal s observer.on_next(s) - s = set[_T]() + s = set() observer.on_completed() return source.subscribe_(s.add, observer.on_error, on_completed, scheduler) diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index eec0b5cd0..d4279013a 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -52,7 +52,7 @@ def subscribe( q: List[Subject[_T]] = [] def create_window(): - s = Subject[_T]() + s: Subject[_T] = Subject() q.append(s) observer.on_next(add_ref(s, refCountDisposable)) From 4d59a445a16dd31b27c6fca6ade31e8e2935d147 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 22:00:14 +0100 Subject: [PATCH 010/103] Stop using _ for operator implementations --- rx/core/operators/amb.py | 6 +- rx/core/operators/asobservable.py | 4 +- rx/core/operators/average.py | 29 ++++++--- rx/core/operators/lastordefault.py | 4 +- rx/core/operators/map.py | 19 +++--- rx/core/operators/materialize.py | 29 ++++++--- rx/core/operators/max.py | 4 +- rx/core/operators/maxby.py | 4 +- rx/core/operators/merge.py | 38 ++++++++--- rx/core/operators/min.py | 10 +-- rx/core/operators/minby.py | 4 +- rx/core/operators/observeon.py | 23 +++++-- rx/core/operators/onerrorresumenext.py | 14 ++++- rx/core/operators/reduce.py | 4 +- rx/core/operators/repeat.py | 16 +++-- rx/core/operators/retry.py | 4 +- rx/core/operators/sample.py | 4 +- rx/operators/__init__.py | 87 +++++++++++++------------- 18 files changed, 192 insertions(+), 111 deletions(-) diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 3359e29bc..1bd187a2c 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -2,13 +2,13 @@ from typing import Callable, List, Optional, Union, cast, TypeVar from rx import from_future -from rx.core import Observable, abc, typing +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable _T = TypeVar("_T") -def _amb( +def amb( right_source: Union[Observable[_T], "Future[_T]"] ) -> Callable[[Observable[_T]], Observable[_T]]: @@ -90,4 +90,4 @@ def on_completed_right() -> None: return amb -__all__ = ["_amb"] +__all__ = ["amb"] diff --git a/rx/core/operators/asobservable.py b/rx/core/operators/asobservable.py index d429277ca..cb37782da 100644 --- a/rx/core/operators/asobservable.py +++ b/rx/core/operators/asobservable.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def _as_observable() -> Callable[[Observable[_T]], Observable[_T]]: +def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: def as_observable(source: Observable[_T]) -> Observable[_T]: """Hides the identity of an observable sequence. @@ -28,4 +28,4 @@ def subscribe( return as_observable -__all__ = ["_as_observable"] +__all__ = ["as_observable"] diff --git a/rx/core/operators/average.py b/rx/core/operators/average.py index 70f6060fc..273f253ba 100644 --- a/rx/core/operators/average.py +++ b/rx/core/operators/average.py @@ -1,18 +1,23 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators from rx.core import Observable from rx.core.typing import Mapper +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") + class AverageValue(object): - def __init__(self, sum, count): + def __init__(self, sum: float, count: int): self.sum = sum self.count = count -def _average(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: - def average(source: Observable) -> Observable: +def average( + key_mapper: Optional[Mapper[_T, _TKey]] = None, +) -> Callable[[Observable[_T]], Observable[float]]: + def average(source: Observable[_T]) -> Observable[float]: """Partially applied average operator. Computes the average of an observable sequence of values that @@ -33,15 +38,15 @@ def average(source: Observable) -> Observable: if key_mapper: return source.pipe( operators.map(key_mapper), - operators.average() + operators.average(), ) - def accumulator(prev, cur): - return AverageValue(sum=prev.sum+cur, count=prev.count+1) + def accumulator(prev: AverageValue, cur: float) -> AverageValue: + return AverageValue(sum=prev.sum + cur, count=prev.count + 1) - def mapper(s): + def mapper(s: AverageValue) -> float: if s.count == 0: - raise Exception('The input sequence was empty') + raise Exception("The input sequence was empty") return s.sum / float(s.count) @@ -49,6 +54,10 @@ def mapper(s): return source.pipe( operators.scan(accumulator, seed), operators.last(), - operators.map(mapper) + operators.map(mapper), ) + return average + + +__all__ = ["average"] diff --git a/rx/core/operators/lastordefault.py b/rx/core/operators/lastordefault.py index 138e91948..ad9304bc6 100644 --- a/rx/core/operators/lastordefault.py +++ b/rx/core/operators/lastordefault.py @@ -30,7 +30,7 @@ def on_completed(): return Observable(subscribe) -def _last_or_default( +def last_or_default( predicate: Optional[typing.Predicate[_T]] = None, default_value: Any = None ) -> Callable[[Observable[_T]], Observable[_T]]: def last_or_default(source: Observable[_T]) -> Observable[_T]: @@ -56,3 +56,5 @@ def last_or_default(source: Observable[_T]) -> Observable[_T]: return last_or_default_async(source, True, default_value) return last_or_default + +__all__ = [ "last_or_default", "last_or_default_async"] \ No newline at end of file diff --git a/rx/core/operators/map.py b/rx/core/operators/map.py index 357bc7c52..3a205c803 100644 --- a/rx/core/operators/map.py +++ b/rx/core/operators/map.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Optional, TypeVar +from typing import Any, Callable, Optional, TypeVar, cast from rx import operators as ops from rx.core import Observable, abc, pipe @@ -9,10 +9,13 @@ _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") + # pylint: disable=redefined-builtin -def _map(mapper: Optional[Mapper[_T1, _T2]] = None) -> Callable[[Observable[_T1]], Observable[_T2]]: +def map( + mapper: Optional[Mapper[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: - _mapper = mapper or identity + _mapper = mapper or cast(Mapper[_T1, _T2], identity) def map(source: Observable[_T1]) -> Observable[_T2]: """Partially applied map operator. @@ -32,8 +35,10 @@ def map(source: Observable[_T1]) -> Observable[_T2]: of the source. """ - def subscribe(obv: abc.ObserverBase[_T2], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: - def on_next(value: Any) -> None: + def subscribe( + obv: abc.ObserverBase[_T2], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + def on_next(value: _T1) -> None: try: result = _mapper(value) except Exception as err: # pylint: disable=broad-except @@ -48,7 +53,7 @@ def on_next(value: Any) -> None: return map -def _map_indexed( +def map_indexed( mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: def _identity(value: Any, _: int) -> Any: @@ -59,4 +64,4 @@ def _identity(value: Any, _: int) -> Any: return pipe(ops.zip_with_iterable(infinite()), ops.starmap_indexed(_mapper_indexed)) -__all__ = ["_map", "_map_indexed"] +__all__ = ["map", "map_indexed"] diff --git a/rx/core/operators/materialize.py b/rx/core/operators/materialize.py index fecb8dc82..c4fbd38d4 100644 --- a/rx/core/operators/materialize.py +++ b/rx/core/operators/materialize.py @@ -1,11 +1,14 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar +from rx.core.notification import Notification -from rx.core import Observable +from rx.core import Observable, abc from rx.core.notification import OnCompleted, OnError, OnNext +_T = TypeVar("_T") -def _materialize() -> Callable[[Observable], Observable]: - def materialize(source: Observable) -> Observable: + +def materialize() -> Callable[[Observable[_T]], Observable[Notification[_T]]]: + def materialize(source: Observable[_T]) -> Observable[Notification[_T]]: """Partially applied materialize operator. Materializes the implicit notifications of an observable @@ -19,18 +22,26 @@ def materialize(source: Observable) -> Observable: notification values from the source sequence. """ - def subscribe(observer, scheduler=None): - def on_next(value): + def subscribe( + observer: abc.ObserverBase[Notification[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ): + def on_next(value: _T) -> None: observer.on_next(OnNext(value)) - def on_error(exception): - observer.on_next(OnError(exception)) + def on_error(error: Exception) -> None: + observer.on_next(OnError(error)) observer.on_completed() - def on_completed(): + def on_completed() -> None: observer.on_next(OnCompleted()) observer.on_completed() return source.subscribe_(on_next, on_error, on_completed, scheduler) + return Observable(subscribe) + return materialize + + +__all__ = ["materialize"] diff --git a/rx/core/operators/max.py b/rx/core/operators/max.py index 1a7898355..0db930437 100644 --- a/rx/core/operators/max.py +++ b/rx/core/operators/max.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def _max( +def max( comparer: Optional[Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the maximum value in an observable sequence according to @@ -32,4 +32,4 @@ def _max( return pipe(ops.max_by(identity, comparer), ops.map(first_only)) -__all__ = ["_max"] +__all__ = ["max"] diff --git a/rx/core/operators/maxby.py b/rx/core/operators/maxby.py index ece95b3a6..147915bc6 100644 --- a/rx/core/operators/maxby.py +++ b/rx/core/operators/maxby.py @@ -9,7 +9,7 @@ _TKey = TypeVar("_TKey") -def _max_by( +def max_by( key_mapper: typing.Mapper[_T, _TKey], comparer: Optional[typing.SubComparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -37,4 +37,4 @@ def max_by(source: Observable[_T]) -> Observable[List[_T]]: return max_by -__all__ = ["_max_by"] +__all__ = ["max_by"] diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index cb7c82dd7..1ad86aeee 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def _merge( +def merge( *sources: Observable[_T], max_concurrent: Optional[int] = None ) -> Callable[[Observable[Observable[_T]]], Observable[_T]]: def merge(source: Observable[Observable[_T]]) -> Observable[_T]: @@ -34,7 +34,10 @@ def merge(source: Observable[Observable[_T]]) -> Observable[_T]: sources_ = tuple([source]) + sources return rx.merge(*sources_) - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): active_count = [0] group = CompositeDisposable() is_stopped = [False] @@ -57,7 +60,9 @@ def on_completed(): on_next = synchronized(source.lock)(observer.on_next) on_error = synchronized(source.lock)(observer.on_error) - subscription.disposable = xs.subscribe_(on_next, on_error, on_completed, scheduler) + subscription.disposable = xs.subscribe_( + on_next, on_error, on_completed, scheduler + ) def on_next(inner_source: Observable[_T]) -> None: if active_count[0] < max_concurrent: @@ -71,7 +76,9 @@ def on_completed(): if active_count[0] == 0: observer.on_completed() - group.add(source.subscribe_(on_next, observer.on_error, on_completed, scheduler)) + group.add( + source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + ) return group return Observable(subscribe) @@ -79,7 +86,7 @@ def on_completed(): return merge -def _merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: +def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: def merge_all(source: Observable[Observable[_T]]) -> Observable[_T]: """Partially applied merge_all operator. @@ -94,7 +101,10 @@ def merge_all(source: Observable[Observable[_T]]) -> Observable[_T]: sequences. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): group = CompositeDisposable() is_stopped = [False] m = SingleAssignmentDisposable() @@ -104,7 +114,11 @@ def on_next(inner_source: Observable[_T]): inner_subscription = SingleAssignmentDisposable() group.add(inner_subscription) - inner_source = from_future(inner_source) if is_future(inner_source) else inner_source + inner_source = ( + from_future(inner_source) + if is_future(inner_source) + else inner_source + ) @synchronized(source.lock) def on_completed(): @@ -114,7 +128,9 @@ def on_completed(): on_next: typing.OnNext[_T] = synchronized(source.lock)(observer.on_next) on_error = synchronized(source.lock)(observer.on_error) - subscription = inner_source.subscribe_(on_next, on_error, on_completed, scheduler) + subscription = inner_source.subscribe_( + on_next, on_error, on_completed, scheduler + ) inner_subscription.disposable = subscription def on_completed(): @@ -122,7 +138,9 @@ def on_completed(): if len(group) == 1: observer.on_completed() - m.disposable = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + m.disposable = source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) return group return Observable(subscribe) @@ -130,4 +148,4 @@ def on_completed(): return merge_all -__all__ = ["_merge", "_merge_all"] +__all__ = ["merge", "merge_all"] diff --git a/rx/core/operators/min.py b/rx/core/operators/min.py index bb83875e3..c31ed3d60 100644 --- a/rx/core/operators/min.py +++ b/rx/core/operators/min.py @@ -16,7 +16,7 @@ def first_only(x: Sequence[_T]) -> _T: return x[0] -def _min( +def min( comparer: Optional[Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """The `min` operator. @@ -35,8 +35,10 @@ def _min( An observable sequence containing a single element with the minimum element in the source sequence. """ + return pipe( + ops.min_by(identity, comparer), + ops.map(first_only), + ) - return pipe(ops.min_by(identity, comparer), ops.map(first_only)) - -__all__ = ["_min"] +__all__ = ["min"] diff --git a/rx/core/operators/minby.py b/rx/core/operators/minby.py index 247a8d33f..efd068b3b 100644 --- a/rx/core/operators/minby.py +++ b/rx/core/operators/minby.py @@ -56,7 +56,7 @@ def on_completed(): return Observable(subscribe) -def _min_by( +def min_by( key_mapper: typing.Mapper[_T, _TKey], comparer: Optional[typing.SubComparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -86,4 +86,4 @@ def min_by(source: Observable[_T]) -> Observable[List[_T]]: return min_by -__all__ = ["_min_by"] +__all__ = ["min_by"] diff --git a/rx/core/operators/observeon.py b/rx/core/operators/observeon.py index 93fa19b43..c65856beb 100644 --- a/rx/core/operators/observeon.py +++ b/rx/core/operators/observeon.py @@ -1,11 +1,16 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar from rx.core import Observable, abc from rx.core.observer import ObserveOnObserver -def _observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: - def observe_on(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def observe_on( + scheduler: abc.SchedulerBase, +) -> Callable[[Observable[_T]], Observable[_T]]: + def observe_on(source: Observable[_T]) -> Observable[_T]: """Wraps the source sequence in order to run its observer callbacks on the specified scheduler. @@ -23,9 +28,17 @@ def observe_on(source: Observable) -> Observable: the specified scheduler. """ - def subscribe(observer, subscribe_scheduler=None): - return source.subscribe(ObserveOnObserver(scheduler, observer), scheduler=subscribe_scheduler) + def subscribe( + observer: abc.ObserverBase[_T], + subscribe_scheduler: Optional[abc.SchedulerBase] = None, + ): + return source.subscribe( + ObserveOnObserver(scheduler, observer), scheduler=subscribe_scheduler + ) return Observable(subscribe) return observe_on + + +__all__ = ["observe_on"] diff --git a/rx/core/operators/onerrorresumenext.py b/rx/core/operators/onerrorresumenext.py index d3d2139bf..9b39a4274 100644 --- a/rx/core/operators/onerrorresumenext.py +++ b/rx/core/operators/onerrorresumenext.py @@ -1,10 +1,20 @@ from typing import Callable +from typing import TypeVar + import rx from rx.core import Observable +_T = TypeVar("_T") + -def _on_error_resume_next(second: Observable) -> Callable[[Observable], Observable]: - def on_error_resume_next(source: Observable) -> Observable: +def on_error_resume_next( + second: Observable[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: + def on_error_resume_next(source: Observable[_T]) -> Observable[_T]: return rx.on_error_resume_next(source, second) + return on_error_resume_next + + +__all__ = ["on_error_resume_next"] diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 9fa1c11af..6059a06bf 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -9,7 +9,7 @@ _TState = TypeVar("_TState") -def _reduce( +def reduce( accumulator: Accumulator[_TState, _T], seed: Any = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """Applies an accumulator function over an observable sequence, @@ -41,4 +41,4 @@ def _reduce( return pipe(ops.scan(accumulator), ops.last()) -__all__ = ["_reduce"] +__all__ = ["reduce"] diff --git a/rx/core/operators/repeat.py b/rx/core/operators/repeat.py index 5519ed14b..461ed5233 100644 --- a/rx/core/operators/repeat.py +++ b/rx/core/operators/repeat.py @@ -1,16 +1,20 @@ import sys -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar import rx from rx.core import Observable from rx.internal.utils import infinite +_T = TypeVar("_T") -def _repeat(repeat_count: Optional[int] = None) -> Callable[[Observable], Observable]: + +def repeat( + repeat_count: Optional[int] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: if repeat_count is None: repeat_count = sys.maxsize - def repeat(source: Observable) -> Observable: + def repeat(source: Observable[_T]) -> Observable[_T]: """Repeats the observable sequence a specified number of times. If the repeat count is not specified, the sequence repeats indefinitely. @@ -32,5 +36,9 @@ def repeat(source: Observable) -> Observable: else: gen = range(repeat_count) - return rx.defer(lambda _: rx.concat_with_iterable(source for _ in gen)) + return rx.defer(lambda _: rx.concat_with_iterable(source for _ in gen)) + return repeat + + +__all = ["repeat"] diff --git a/rx/core/operators/retry.py b/rx/core/operators/retry.py index d4a4b95dd..ce01e0b3c 100644 --- a/rx/core/operators/retry.py +++ b/rx/core/operators/retry.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _retry( +def retry( retry_count: Optional[int] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the source observable sequence the specified number of @@ -38,4 +38,4 @@ def retry(source: Observable[_T]) -> Observable[_T]: return retry -__all__ = ["_retry"] +__all__ = ["retry"] diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index 2ac74cfeb..2c06f1725 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -45,7 +45,7 @@ def on_completed(): return Observable(subscribe) -def _sample( +def sample( sampler: Union[typing.RelativeTime, Observable[Any]], scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: @@ -70,4 +70,4 @@ def sample(source: Observable[_T]) -> Observable[_T]: return sample -__all__ = ["_sample"] +__all__ = ["sample"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index aa8bab82a..3e5fd65f1 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1,7 +1,6 @@ # pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin from asyncio import Future -from tkinter import NO from typing import ( Any, Callable, @@ -90,9 +89,9 @@ def amb(right_source: Observable[_T]) -> Callable[[Observable[_T]], Observable[_ returns an observable sequence that surfaces any of the given sequences, whichever reacted first. """ - from rx.core.operators.amb import _amb + from rx.core.operators.amb import amb - return _amb(right_source) + return amb(right_source) def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: @@ -103,12 +102,14 @@ def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: returns and observable sequence that hides the identity of the source sequence. """ - from rx.core.operators.asobservable import _as_observable + from rx.core.operators.asobservable import as_observable - return _as_observable() + return as_observable() -def average(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def average( + key_mapper: Optional[Mapper[_T, _TKey]] = None +) -> Callable[[Observable[_T]], Observable[float]]: """The average operator. Computes the average of an observable sequence of values that @@ -134,9 +135,9 @@ def average(key_mapper: Optional[Mapper] = None) -> Callable[[Observable], Obser returns an observable sequence containing a single element with the average of the sequence of values. """ - from rx.core.operators.average import _average + from rx.core.operators.average import average - return _average(key_mapper) + return average(key_mapper) def buffer(boundaries: Observable) -> Callable[[Observable], Observable]: @@ -1576,9 +1577,9 @@ def last_or_default( the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. """ - from rx.core.operators.lastordefault import _last_or_default + from rx.core.operators.lastordefault import last_or_default - return _last_or_default(predicate, default_value) + return last_or_default(predicate, default_value) def map( @@ -1608,9 +1609,9 @@ def map( the result of invoking the transform function on each element of the source. """ - from rx.core.operators.map import _map + from rx.core.operators.map import map - return _map(mapper) + return map(mapper) def map_indexed( @@ -1640,9 +1641,9 @@ def map_indexed( the result of invoking the transform function on each element of the source. """ - from rx.core.operators.map import _map_indexed + from rx.core.operators.map import map_indexed - return _map_indexed(mapper_indexed) + return map_indexed(mapper_indexed) def materialize() -> Callable[[Observable[_T]], Observable[Notification[_T]]]: @@ -1654,9 +1655,9 @@ def materialize() -> Callable[[Observable[_T]], Observable[Notification[_T]]]: returns an observable sequence containing the materialized notification values from the source sequence. """ - from rx.core.operators.materialize import _materialize + from rx.core.operators.materialize import materialize - return _materialize() + return materialize() def max( @@ -1684,9 +1685,9 @@ def max( source and returns an observable sequence containing a single element with the maximum element in the source sequence. """ - from rx.core.operators.max import _max + from rx.core.operators.max import max - return _max(comparer) + return max(comparer) def max_by( @@ -1717,9 +1718,9 @@ def max_by( source and return an observable sequence containing a list of zero or more elements that have a maximum key value. """ - from rx.core.operators.maxby import _max_by + from rx.core.operators.maxby import max_by - return _max_by(key_mapper, comparer) + return max_by(key_mapper, comparer) def merge( @@ -1752,9 +1753,9 @@ def merge( returns the observable sequence that merges the elements of the inner sequences. """ - from rx.core.operators.merge import _merge + from rx.core.operators.merge import merge - return _merge(*sources, max_concurrent=max_concurrent) + return merge(*sources, max_concurrent=max_concurrent) def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: @@ -1776,9 +1777,9 @@ def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: source and returns the observable sequence that merges the elements of the inner sequences. """ - from rx.core.operators.merge import _merge_all + from rx.core.operators.merge import merge_all - return _merge_all() + return merge_all() def min( @@ -1808,9 +1809,9 @@ def min( returns an observable sequence containing a single element with the minimum element in the source sequence. """ - from rx.core.operators.min import _min + from rx.core.operators.min import min - return _min(comparer) + return min(comparer) def min_by( @@ -1841,9 +1842,9 @@ def min_by( reuturns an observable sequence containing a list of zero or more elements that have a minimum key value. """ - from rx.core.operators.minby import _min_by + from rx.core.operators.minby import min_by - return _min_by(key_mapper, comparer) + return min_by(key_mapper, comparer) def multicast( @@ -1901,12 +1902,14 @@ def observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observabl returns the source sequence whose observations happen on the specified scheduler. """ - from rx.core.operators.observeon import _observe_on + from rx.core.operators.observeon import observe_on - return _observe_on(scheduler) + return observe_on(scheduler) -def on_error_resume_next(second: Observable) -> Callable[[Observable], Observable]: +def on_error_resume_next( + second: Observable[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: """Continues an observable sequence that is terminated normally or by an exception with the next observable sequence. @@ -1929,9 +1932,9 @@ def on_error_resume_next(second: Observable) -> Callable[[Observable], Observabl exceptionally. """ - from rx.core.operators.onerrorresumenext import _on_error_resume_next + from rx.core.operators.onerrorresumenext import on_error_resume_next - return _on_error_resume_next(second) + return on_error_resume_next(second) def pairwise() -> Callable[[Observable], Observable]: @@ -2161,9 +2164,9 @@ def reduce( source and returns an observable sequence containing a single element with the final accumulator value. """ - from rx.core.operators.reduce import _reduce + from rx.core.operators.reduce import reduce - return _reduce(accumulator, seed) + return reduce(accumulator, seed) def ref_count() -> Callable[[ConnectableObservable], Observable]: @@ -2203,9 +2206,9 @@ def repeat( returns an observable sequence producing the elements of the given sequence repeatedly. """ - from rx.core.operators.repeat import _repeat + from rx.core.operators.repeat import repeat - return _repeat(repeat_count) + return repeat(repeat_count) def replay( @@ -2272,9 +2275,9 @@ def retry( An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully. """ - from rx.core.operators.retry import _retry + from rx.core.operators.retry import retry - return _retry(retry_count) + return retry(retry_count) def sample( @@ -2304,13 +2307,13 @@ def sample( An operator function that takes an observable source and returns a sampled observable sequence. """ - from rx.core.operators.sample import _sample + from rx.core.operators.sample import sample - return _sample(sampler, scheduler) + return sample(sampler, scheduler) def scan( - accumulator: Accumulator[_TState, _T], seed: Union[_T, NotSet] = NotSet + accumulator: Accumulator[_TState, _T], seed: Union[_TState, NotSet] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """The scan operator. From 79837491fbef6bc65a5192bcc8a74d6736f5eb4a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 4 Feb 2022 22:59:22 +0100 Subject: [PATCH 011/103] Fix flaky test --- rx/__init__.py | 12 ++-- rx/core/observable/start.py | 4 +- rx/core/observable/toasync.py | 17 +++-- rx/core/operators/publish.py | 11 +++- rx/core/operators/single.py | 4 +- rx/core/operators/singleordefault.py | 8 +-- rx/core/operators/skip.py | 4 +- rx/core/operators/skiplast.py | 13 ++-- rx/core/operators/skiplastwithtime.py | 4 +- rx/core/operators/skipuntil.py | 4 +- rx/core/operators/skipwhile.py | 19 ++++-- rx/core/operators/startswith.py | 4 +- rx/core/operators/subscribeon.py | 20 ++++-- rx/core/operators/switchlatest.py | 22 +++++-- rx/core/operators/timeoutwithmapper.py | 36 ++++++---- rx/core/operators/timestamp.py | 4 +- rx/operators/__init__.py | 66 ++++++++++--------- tests/test_observable/test_concat.py | 53 ++++++++------- .../test_scheduler/test_newthreadscheduler.py | 7 +- 19 files changed, 189 insertions(+), 123 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index b45bdf084..93954448b 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1003,9 +1003,9 @@ def start( An observable sequence exposing the function's result value, or an exception. """ - from .core.observable.start import _start + from .core.observable.start import start - return _start(func, scheduler) + return start(func, scheduler) def start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: @@ -1102,8 +1102,8 @@ def timer( def to_async( - func: Callable[..., Any], scheduler: Optional[abc.SchedulerBase] = None -) -> Callable: + 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 @@ -1129,9 +1129,9 @@ def to_async( Returns: Asynchronous function. """ - from .core.observable.toasync import _to_async + from .core.observable.toasync import to_async - return _to_async(func, scheduler) + return to_async(func, scheduler) def using( diff --git a/rx/core/observable/start.py b/rx/core/observable/start.py index f3974e28f..41d885a77 100644 --- a/rx/core/observable/start.py +++ b/rx/core/observable/start.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _start( +def start( func: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None ) -> Observable[_T]: """Invokes the specified function asynchronously on the specified @@ -34,4 +34,4 @@ def _start( return to_async(func, scheduler)() -__all__ = ["_start"] +__all__ = ["start"] diff --git a/rx/core/observable/toasync.py b/rx/core/observable/toasync.py index f8d90d7fd..c47db5017 100644 --- a/rx/core/observable/toasync.py +++ b/rx/core/observable/toasync.py @@ -1,12 +1,16 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, Any from rx import operators as ops from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler from rx.subject import AsyncSubject +_T = TypeVar("_T") -def _to_async(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> Callable: + +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 @@ -28,10 +32,10 @@ def _to_async(func: Callable, scheduler: Optional[abc.SchedulerBase] = None) -> _scheduler = scheduler or TimeoutScheduler.singleton() - def wrapper(*args) -> Observable: - subject = AsyncSubject() + def wrapper(*args: Any) -> Observable[_T]: + subject: AsyncSubject[_T] = AsyncSubject() - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any = None) -> None: try: result = func(*args) except Exception as ex: # pylint: disable=broad-except @@ -45,3 +49,6 @@ def action(scheduler, state): return subject.pipe(ops.as_observable()) return wrapper + + +__all__ = ["to_async"] diff --git a/rx/core/operators/publish.py b/rx/core/operators/publish.py index 3cee93efa..79a327d99 100644 --- a/rx/core/operators/publish.py +++ b/rx/core/operators/publish.py @@ -1,12 +1,17 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import ConnectableObservable, Observable, pipe from rx.core.typing import Mapper from rx.subject import Subject +_T = TypeVar("_T") +_TResult = TypeVar("_TResult") -def _publish(mapper: Optional[Mapper] = None) -> Callable[[Observable], ConnectableObservable]: + +def _publish( + mapper: Optional[Mapper[_T, _TResult]] = None, +) -> Callable[[Observable[_T]], ConnectableObservable[_TResult]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence. This operator is a @@ -34,7 +39,7 @@ def _publish(mapper: Optional[Mapper] = None) -> Callable[[Observable], Connecta return pipe(ops.multicast(subject=Subject())) -def _share() -> Callable[[Observable], Observable]: +def share() -> Callable[[Observable[_T]], Observable[_T]]: """Share a single subscription among multple observers. Returns a new Observable that multicasts (shares) the original diff --git a/rx/core/operators/single.py b/rx/core/operators/single.py index dd71aef83..e77a3f3db 100644 --- a/rx/core/operators/single.py +++ b/rx/core/operators/single.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _single( +def single( predicate: Optional[Predicate[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that satisfies the @@ -33,4 +33,4 @@ def _single( return ops.single_or_default_async(False) -__all__ = ["_single"] +__all__ = ["single"] diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index a8efa349e..9f755db97 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _single_or_default_async( +def single_or_default_async( has_default: bool = False, default_value: _T = None ) -> Callable[[Observable[_T]], Observable[_T]]: def single_or_default_async(source: Observable[_T]) -> Observable[_T]: @@ -44,7 +44,7 @@ def on_completed(): return single_or_default_async -def _single_or_default( +def single_or_default( predicate: Optional[Predicate[_T]] = None, default_value: _T = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that matches @@ -73,7 +73,7 @@ def _single_or_default( if predicate: return pipe(ops.filter(predicate), ops.single_or_default(None, default_value)) else: - return _single_or_default_async(True, default_value) + return single_or_default_async(True, default_value) -__all__ = ["_single_or_default", "_single_or_default_async"] +__all__ = ["single_or_default", "single_or_default_async"] diff --git a/rx/core/operators/skip.py b/rx/core/operators/skip.py index c0649534f..af8f1bab5 100644 --- a/rx/core/operators/skip.py +++ b/rx/core/operators/skip.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: if count < 0: raise ArgumentOutOfRangeException() @@ -47,4 +47,4 @@ def on_next(value: _T) -> None: return skip -__all__ = ["_skip"] +__all__ = ["skip"] diff --git a/rx/core/operators/skiplast.py b/rx/core/operators/skiplast.py index 14d200ea1..dda41ac47 100644 --- a/rx/core/operators/skiplast.py +++ b/rx/core/operators/skiplast.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def _skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: def skip_last(source: Observable[_T]) -> Observable[_T]: """Bypasses a specified number of elements at the end of an observable sequence. @@ -24,7 +24,10 @@ def skip_last(source: Observable[_T]) -> Observable[_T]: elements except for the bypassed ones at the end. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): q: List[_T] = [] def on_next(value: _T) -> None: @@ -37,11 +40,13 @@ def on_next(value: _T) -> None: if front is not None: observer.on_next(front) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) return skip_last -__all__ = ["_skip_last"] +__all__ = ["skip_last"] diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index 21827a6b2..e97802ee4 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _skip_last_with_time( +def skip_last_with_time( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements for the specified duration from the end of the @@ -66,4 +66,4 @@ def on_completed() -> None: return skip_last_with_time -__all__ = ["_skip_last_with_time"] +__all__ = ["skip_last_with_time"] diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index 715fd96b2..67db07843 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -9,7 +9,7 @@ _T = TypeVar("_T") -def _skip_until( +def skip_until( other: Union[Observable[_T], "Future[_T]"] ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after @@ -71,4 +71,4 @@ def on_completed2(): return skip_until -__all__ = ["_skip_until"] +__all__ = ["skip_until"] diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index a0f20905e..7a702f95b 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -6,7 +6,9 @@ _T = TypeVar("_T") -def _skip_while(predicate: typing.Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_while( + predicate: typing.Predicate[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: def skip_while(source: Observable[_T]) -> Observable[_T]: """Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining @@ -25,7 +27,10 @@ def skip_while(source: Observable[_T]) -> Observable[_T]: series that does not pass the test specified by predicate. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): running = False def on_next(value: _T): @@ -41,14 +46,18 @@ def on_next(value: _T): if running: observer.on_next(value) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) return skip_while -def _skip_while_indexed(predicate: typing.PredicateIndexed[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_while_indexed( + predicate: typing.PredicateIndexed[_T], +) -> Callable[[Observable[_T]], Observable[_T]]: return pipe( ops.map_indexed(lambda x, i: (x, i)), ops.skip_while(lambda x: predicate(*x)), @@ -56,4 +65,4 @@ def _skip_while_indexed(predicate: typing.PredicateIndexed[_T]) -> Callable[[Obs ) -__all__ = ["_skip_while", "_skip_while_indexed"] +__all__ = ["skip_while", "skip_while_indexed"] diff --git a/rx/core/operators/startswith.py b/rx/core/operators/startswith.py index 8249c142a..30a896500 100644 --- a/rx/core/operators/startswith.py +++ b/rx/core/operators/startswith.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: +def start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: def start_with(source: Observable[_T]) -> Observable[_T]: """Partially applied start_with operator. @@ -25,4 +25,4 @@ def start_with(source: Observable[_T]) -> Observable[_T]: return start_with -__all__ = ["_start_with"] +__all__ = ["start_with"] diff --git a/rx/core/operators/subscribeon.py b/rx/core/operators/subscribeon.py index 419af02de..578b338da 100644 --- a/rx/core/operators/subscribeon.py +++ b/rx/core/operators/subscribeon.py @@ -1,12 +1,18 @@ from typing import Callable, TypeVar, Optional, Any from rx.core import Observable, abc -from rx.disposable import ScheduledDisposable, SerialDisposable, SingleAssignmentDisposable +from rx.disposable import ( + ScheduledDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) _T = TypeVar("_T") -def _subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable[_T]], Observable[_T]]: +def subscribe_on( + scheduler: abc.SchedulerBase, +) -> Callable[[Observable[_T]], Observable[_T]]: def subscribe_on(source: Observable[_T]) -> Observable[_T]: """Subscribe on the specified scheduler. @@ -28,13 +34,17 @@ def subscribe_on(source: Observable[_T]) -> Observable[_T]: un-subscriptions happen on the specified scheduler. """ - def subscribe(observer: abc.ObserverBase[_T], _: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], _: Optional[abc.SchedulerBase] = None + ): m = SingleAssignmentDisposable() d = SerialDisposable() d.disposable = m def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): - d.disposable = ScheduledDisposable(scheduler, source.subscribe(observer)) + d.disposable = ScheduledDisposable( + scheduler, source.subscribe(observer) + ) m.disposable = scheduler.schedule(action) return d @@ -44,4 +54,4 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): return subscribe_on -__all__ = ["_subscribe_on"] +__all__ = ["subscribe_on"] diff --git a/rx/core/operators/switchlatest.py b/rx/core/operators/switchlatest.py index 7c2d72fe6..2e9f73322 100644 --- a/rx/core/operators/switchlatest.py +++ b/rx/core/operators/switchlatest.py @@ -3,12 +3,15 @@ from rx import from_future from rx.core import Observable -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.internal.utils import is_future -def _switch_latest() -> Callable[[Observable], Observable]: +def switch_latest() -> Callable[[Observable], Observable]: def switch_latest(source: Observable) -> Observable: """Partially applied switch_latest operator. @@ -58,14 +61,23 @@ def on_completed() -> None: if is_stopped[0]: observer.on_completed() - d.disposable = obs.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) + d.disposable = obs.subscribe_( + on_next, on_error, on_completed, scheduler=scheduler + ) def on_completed() -> None: is_stopped[0] = True if not has_latest[0]: observer.on_completed() - subscription = source.subscribe_(on_next, observer.on_error, on_completed, scheduler=scheduler) + subscription = source.subscribe_( + on_next, observer.on_error, on_completed, scheduler=scheduler + ) return CompositeDisposable(subscription, inner_subscription) + return Observable(subscribe) + return switch_latest + + +__all__ = ["switch_latest"] diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index 20ec6e38e..97402cec7 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -2,14 +2,18 @@ import rx from rx.core import Observable -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) - - -def _timeout_with_mapper(first_timeout: Optional[Observable] = None, - timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, - other: Optional[Observable] = None - ) -> Callable[[Observable], Observable]: +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) + + +def timeout_with_mapper( + first_timeout: Optional[Observable] = None, + timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, + other: Optional[Observable] = None, +) -> Callable[[Observable], Observable]: """Returns the source observable sequence, switching to the other observable sequence if a timeout is signaled. @@ -33,7 +37,7 @@ def _timeout_with_mapper(first_timeout: Optional[Observable] = None, """ first_timeout = first_timeout or rx.never() - other = other or rx.throw(Exception('Timeout')) + other = other or rx.throw(Exception("Timeout")) def timeout_with_mapper(source: Observable) -> Observable: def subscribe(observer, scheduler=None): @@ -57,7 +61,9 @@ def timer_wins(): def on_next(x): if timer_wins(): - subscription.disposable = other.subscribe(observer, scheduler=scheduler) + subscription.disposable = other.subscribe( + observer, scheduler=scheduler + ) d.dispose() @@ -69,7 +75,9 @@ def on_completed(): if timer_wins(): subscription.disposable = other.subscribe(observer) - d.disposable = timeout.subscribe_(on_next, on_error, on_completed, scheduler) + d.disposable = timeout.subscribe_( + on_next, on_error, on_completed, scheduler + ) set_timer(first_timeout) @@ -100,7 +108,11 @@ def on_completed(): if observer_wins(): observer.on_completed() - original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler) + original.disposable = source.subscribe_( + on_next, on_error, on_completed, scheduler + ) return CompositeDisposable(subscription, timer) + return Observable(subscribe) + return timeout_with_mapper diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index 58d66b56a..64a432967 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -11,7 +11,7 @@ class Timestamp(NamedTuple): timestamp: datetime -def _timestamp( +def timestamp( scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[Any]], Observable[Timestamp]]: def timestamp(source: Observable[Any]) -> Observable[Timestamp]: @@ -43,4 +43,4 @@ def factory(scheduler_: Optional[abc.SchedulerBase] = None): return timestamp -__all__ = ["_timestamp"] +__all__ = ["timestamp"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 3e5fd65f1..e660d6bba 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2399,9 +2399,9 @@ def share() -> Callable[[Observable[_T]], Observable[_T]]: source Observable. """ - from rx.core.operators.publish import _share + from rx.core.operators.publish import share - return _share() + return share() def single( @@ -2434,9 +2434,9 @@ def single( the observable sequence that satisfies the condition in the predicate. """ - from rx.core.operators.single import _single + from rx.core.operators.single import single - return _single(predicate) + return single(predicate) def single_or_default( @@ -2472,17 +2472,17 @@ def single_or_default( the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. """ - from rx.core.operators.singleordefault import _single_or_default + from rx.core.operators.singleordefault import single_or_default - return _single_or_default(predicate, default_value) + return single_or_default(predicate, default_value) def single_or_default_async( has_default: bool = False, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: - from rx.core.operators.singleordefault import _single_or_default_async + from rx.core.operators.singleordefault import single_or_default_async - return _single_or_default_async(has_default, default_value) + return single_or_default_async(has_default, default_value) def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2508,9 +2508,9 @@ def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence that contains the elements that occur after the specified index in the input sequence. """ - from rx.core.operators.skip import _skip + from rx.core.operators.skip import skip - return _skip(count) + return skip(count) def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2541,9 +2541,9 @@ def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence containing the source sequence elements except for the bypassed ones at the end. """ - from rx.core.operators.skiplast import _skip_last + from rx.core.operators.skiplast import skip_last - return _skip_last(count) + return skip_last(count) def skip_last_with_time( @@ -2570,9 +2570,9 @@ def skip_last_with_time( An observable sequence with the elements skipped during the specified duration from the end of the source sequence. """ - from rx.core.operators.skiplastwithtime import _skip_last_with_time + from rx.core.operators.skiplastwithtime import skip_last_with_time - return _skip_last_with_time(duration, scheduler=scheduler) + return skip_last_with_time(duration, scheduler=scheduler) def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2597,9 +2597,9 @@ def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[ source sequence starting from the point the other sequence triggered propagation. """ - from rx.core.operators.skipuntil import _skip_until + from rx.core.operators.skipuntil import skip_until - return _skip_until(other) + return skip_until(other) def skip_until_with_time( @@ -2667,9 +2667,9 @@ def skip_while( the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. """ - from rx.core.operators.skipwhile import _skip_while + from rx.core.operators.skipwhile import skip_while - return _skip_while(predicate) + return skip_while(predicate) def skip_while_indexed( @@ -2701,9 +2701,9 @@ def skip_while_indexed( the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. """ - from rx.core.operators.skipwhile import _skip_while_indexed + from rx.core.operators.skipwhile import skip_while_indexed - return _skip_while_indexed(predicate) + return skip_while_indexed(predicate) def skip_with_time( @@ -2907,12 +2907,14 @@ def start_with(*args: Any) -> Callable[[Observable], Observable]: An operator function that takes a source observable and returns the source sequence prepended with the specified values. """ - from rx.core.operators.startswith import _start_with + from rx.core.operators.startswith import start_with - return _start_with(*args) + return start_with(*args) -def subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: +def subscribe_on( + scheduler: abc.SchedulerBase, +) -> Callable[[Observable[_T]], Observable[_T]]: """Subscribe on the specified scheduler. Wrap the source sequence in order to run its subscription and @@ -2933,9 +2935,9 @@ def subscribe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observa returns the source sequence whose subscriptions and un-subscriptions happen on the specified scheduler. """ - from rx.core.operators.subscribeon import _subscribe_on + from rx.core.operators.subscribeon import subscribe_on - return _subscribe_on(scheduler) + return subscribe_on(scheduler) def sum( @@ -2993,9 +2995,9 @@ def switch_latest() -> Callable[[Observable[Observable[Any]]], Observable[Any]]: time produces the elements of the most recent inner observable sequence that has been received. """ - from rx.core.operators.switchlatest import _switch_latest + from rx.core.operators.switchlatest import switch_latest - return _switch_latest() + return switch_latest() def take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3345,7 +3347,7 @@ def throttle_with_mapper( def timestamp( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[Any]]: """The timestamp operator. Records the timestamp for each value in an observable sequence. @@ -3361,9 +3363,9 @@ def timestamp( source and returns an observable sequence with timestamp information on values. """ - from rx.core.operators.timestamp import _timestamp + from rx.core.operators.timestamp import timestamp - return _timestamp(scheduler=scheduler) + return timestamp(scheduler=scheduler) def timeout( @@ -3433,9 +3435,9 @@ def timeout_with_mapper( returns the source sequence switching to the other sequence in case of a timeout. """ - from rx.core.operators.timeoutwithmapper import _timeout_with_mapper + from rx.core.operators.timeoutwithmapper import timeout_with_mapper - return _timeout_with_mapper(first_timeout, timeout_duration_mapper, other) + return timeout_with_mapper(first_timeout, timeout_duration_mapper, other) def time_interval( diff --git a/tests/test_observable/test_concat.py b/tests/test_observable/test_concat.py index 4a7c99e08..b659288bd 100644 --- a/tests/test_observable/test_concat.py +++ b/tests/test_observable/test_concat.py @@ -18,8 +18,8 @@ class RxException(Exception): # Helper function for raising exceptions within lambdas -def _raise(ex): - raise RxException(ex) +def _raise(error: str): + raise RxException(error) class TestConcat(unittest.TestCase): @@ -72,7 +72,7 @@ def create(): assert results.messages == [] def test_concat_empty_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_completed(230)] msgs2 = [on_next(150, 1), on_error(250, ex)] @@ -86,7 +86,7 @@ def create(): assert results.messages == [on_error(250, ex)] def test_concat_throw_empty(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_error(230, ex)] msgs2 = [on_next(150, 1), on_completed(250)] @@ -100,10 +100,10 @@ def create(): assert results.messages == [on_error(230, ex)] def test_concat_throw_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_error(230, ex)] - msgs2 = [on_next(150, 1), on_error(250, 'ex2')] + msgs2 = [on_next(150, 1), on_error(250, "ex2")] e1 = scheduler.create_hot_observable(msgs1) e2 = scheduler.create_hot_observable(msgs2) @@ -177,7 +177,7 @@ def create(): assert results.messages == [on_next(220, 2), on_next(240, 3), on_completed(250)] def test_concat_throw_return(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_error(230, ex)] msgs2 = [on_next(150, 1), on_next(240, 2), on_completed(250)] @@ -191,7 +191,7 @@ def create(): assert results.messages == [on_error(230, ex)] def test_concat_return_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(220, 2), on_completed(230)] msgs2 = [on_next(150, 1), on_error(250, ex)] @@ -215,19 +215,24 @@ def create(): return e1.pipe(ops.concat(e2)) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next( - 220, 3), on_next(230, 4), on_next(240, 5), on_completed(250)] + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ] def test_concat_forward_scheduler(self): scheduler = TestScheduler() - subscribe_schedulers = {'e1': 'unknown', 'e2': 'unknown'} + subscribe_schedulers = {"e1": "unknown", "e2": "unknown"} - def subscribe_e1(observer, scheduler='not_set'): - subscribe_schedulers['e1'] = scheduler + def subscribe_e1(observer, scheduler="not_set"): + subscribe_schedulers["e1"] = scheduler observer.on_completed() - def subscribe_e2(observer, scheduler='not_set'): - subscribe_schedulers['e2'] = scheduler + def subscribe_e2(observer, scheduler="not_set"): + subscribe_schedulers["e2"] = scheduler observer.on_completed() e1 = rx.create(subscribe_e1) @@ -236,18 +241,18 @@ def subscribe_e2(observer, scheduler='not_set'): stream = e1.pipe(ops.concat(e2)) stream.subscribe(scheduler=scheduler) scheduler.advance_to(1000) - assert subscribe_schedulers['e1'] is scheduler - assert subscribe_schedulers['e2'] is scheduler + assert subscribe_schedulers["e1"] is scheduler + assert subscribe_schedulers["e2"] is scheduler def test_concat_forward_none_scheduler(self): - subscribe_schedulers = {'e1': 'unknown', 'e2': 'unknown'} + subscribe_schedulers = {"e1": "unknown", "e2": "unknown"} - def subscribe_e1(observer, scheduler='not_set'): - subscribe_schedulers['e1'] = scheduler + def subscribe_e1(observer, scheduler="not_set"): + subscribe_schedulers["e1"] = scheduler observer.on_completed() - def subscribe_e2(observer, scheduler='not_set'): - subscribe_schedulers['e2'] = scheduler + def subscribe_e2(observer, scheduler="not_set"): + subscribe_schedulers["e2"] = scheduler observer.on_completed() e1 = rx.create(subscribe_e1) @@ -255,5 +260,5 @@ def subscribe_e2(observer, scheduler='not_set'): stream = e1.pipe(ops.concat(e2)) stream.subscribe() - assert subscribe_schedulers['e1'] is None - assert subscribe_schedulers['e2'] is None + assert subscribe_schedulers["e1"] is None + assert subscribe_schedulers["e2"] is None diff --git a/tests/test_scheduler/test_newthreadscheduler.py b/tests/test_scheduler/test_newthreadscheduler.py index eb7d46f7a..90f349a69 100644 --- a/tests/test_scheduler/test_newthreadscheduler.py +++ b/tests/test_scheduler/test_newthreadscheduler.py @@ -9,7 +9,6 @@ class TestNewThreadScheduler(unittest.TestCase): - def test_new_thread_now(self): scheduler = NewThreadScheduler() diff = scheduler.now - default_now() @@ -62,7 +61,7 @@ def action(scheduler, state): d = scheduler.schedule_relative(timedelta(milliseconds=1), action) d.dispose() - sleep(0.1) + sleep(0.2) assert ran is False def test_new_thread_schedule_periodic(self): @@ -71,7 +70,7 @@ def test_new_thread_schedule_periodic(self): period = 0.05 counter = 3 - def action(state): + def action(state: int): nonlocal counter if state: counter -= 1 @@ -88,7 +87,7 @@ def test_new_thread_schedule_periodic_cancel(self): period = 0.1 counter = 4 - def action(state): + def action(state: int): nonlocal counter if state: counter -= 1 From bf422834a9aa00f02a4eb159ffae5c95dfaf9e3a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 08:20:28 +0100 Subject: [PATCH 012/103] Fix timeout operators --- rx/core/observable/observable.py | 4 +- rx/core/operators/all.py | 4 +- rx/core/operators/buffer.py | 9 +- rx/core/operators/debounce.py | 48 ++++++----- rx/core/operators/throttlefirst.py | 32 ++++--- rx/core/operators/timeinterval.py | 19 +++-- rx/core/operators/timeout.py | 47 +++++++---- rx/core/operators/timeoutwithmapper.py | 38 +++++---- rx/core/operators/todict.py | 30 +++++-- rx/core/operators/tofuture.py | 4 +- rx/core/operators/toiterable.py | 10 +-- rx/core/operators/tomarbles.py | 22 +++-- rx/core/operators/toset.py | 4 +- rx/core/operators/whiledo.py | 4 +- rx/core/operators/withlatestfrom.py | 12 ++- rx/core/operators/zip.py | 6 +- rx/operators/__init__.py | 112 +++++++++++++------------ 17 files changed, 244 insertions(+), 161 deletions(-) diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 55043f069..bc275c519 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -334,10 +334,10 @@ def __await__(self) -> Iterable[_T]: Returns: The last item of the observable sequence. """ - from ..operators.tofuture import _to_future + from ..operators.tofuture import to_future loop = asyncio.get_event_loop() - return iter(self.pipe(_to_future(scheduler=AsyncIOScheduler(loop=loop)))) + return iter(self.pipe(to_future(scheduler=AsyncIOScheduler(loop=loop)))) def __add__(self, other: Observable[_T]) -> Observable[_T]: """Pythonic version of :func:`concat `. diff --git a/rx/core/operators/all.py b/rx/core/operators/all.py index 1c5b02e18..734bec689 100644 --- a/rx/core/operators/all.py +++ b/rx/core/operators/all.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: +def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: filtering = ops.filter(lambda v: not predicate(v)) mapping = ops.map(lambda b: not b) @@ -16,4 +16,4 @@ def _all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool return pipe(filtering, some, mapping) -__all__ = ["_all"] +__all__ = ["all"] diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index 30e8ae5b2..0583ca4d0 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -6,7 +6,9 @@ _T = TypeVar("_T") -def _buffer(boundaries: Observable) -> Callable[[Observable], Observable]: +def buffer( + boundaries: Observable[Any], +) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window(boundaries), ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) ) @@ -30,7 +32,7 @@ def _buffer_toggle( ) -def _buffer_with_count( +def buffer_with_count( count: int, skip: Optional[int] = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Projects each element of an observable sequence into zero or more @@ -70,3 +72,6 @@ def predicate(value: List[_T]) -> bool: ) return buffer_with_count + + +__all__ = ["buffer", "buffer_with_count"] diff --git a/rx/core/operators/debounce.py b/rx/core/operators/debounce.py index e1f6e3057..2f5eaf02e 100644 --- a/rx/core/operators/debounce.py +++ b/rx/core/operators/debounce.py @@ -11,8 +11,8 @@ _T = TypeVar("_T") -def _debounce( - duetime: typing.RelativeTime, scheduler: abc.SchedulerBase +def debounce( + duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] ) -> Callable[[Observable[_T]], Observable[_T]]: def debounce(source: Observable[_T]) -> Observable[_T]: """Ignores values from an observable sequence which are followed by @@ -79,8 +79,8 @@ def on_completed() -> None: return debounce -def _throttle_with_mapper( - throttle_duration_mapper: Callable[[Any], Observable[_T]] +def throttle_with_mapper( + throttle_duration_mapper: Callable[[Any], Observable[Any]] ) -> Callable[[Observable[_T]], Observable[_T]]: def throttle_with_mapper(source: Observable[_T]) -> Observable[_T]: """Partially applied throttle_with_mapper operator. @@ -103,11 +103,13 @@ def subscribe( scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: cancelable = SerialDisposable() - has_value = [False] - value = [None] + has_value: bool = False + value: _T = cast(_T, None) _id = [0] - def on_next(x): + def on_next(x: _T) -> None: + nonlocal value, has_value + throttle = None try: throttle = throttle_duration_mapper(x) @@ -115,25 +117,27 @@ def on_next(x): observer.on_error(e) return - has_value[0] = True - value[0] = x + has_value = True + value = x _id[0] += 1 current_id = _id[0] d = SingleAssignmentDisposable() cancelable.disposable = d - def on_next(x: _T) -> None: - if has_value[0] and _id[0] == current_id: - observer.on_next(value[0]) + def on_next(x: Any) -> None: + nonlocal has_value + if has_value and _id[0] == current_id: + observer.on_next(value) - has_value[0] = False + has_value = False d.dispose() def on_completed() -> None: - if has_value[0] and _id[0] == current_id: - observer.on_next(value[0]) + nonlocal has_value + if has_value and _id[0] == current_id: + observer.on_next(value) - has_value[0] = False + has_value = False d.dispose() d.disposable = throttle.subscribe_( @@ -141,18 +145,20 @@ def on_completed() -> None: ) def on_error(e: Exception) -> None: + nonlocal has_value cancelable.dispose() observer.on_error(e) - has_value[0] = False + has_value = False _id[0] += 1 def on_completed() -> None: + nonlocal has_value cancelable.dispose() - if has_value[0]: - observer.on_next(value[0]) + if has_value: + observer.on_next(value) observer.on_completed() - has_value[0] = False + has_value = False _id[0] += 1 subscription = source.subscribe_( @@ -165,4 +171,4 @@ def on_completed() -> None: return throttle_with_mapper -__all__ = ["_debounce", "_throttle_with_mapper"] +__all__ = ["debounce", "throttle_with_mapper"] diff --git a/rx/core/operators/throttlefirst.py b/rx/core/operators/throttlefirst.py index 308fca02c..5b8360fb3 100644 --- a/rx/core/operators/throttlefirst.py +++ b/rx/core/operators/throttlefirst.py @@ -1,13 +1,16 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar +from datetime import datetime from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _throttle_first( + +def throttle_first( window_duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def throttle_first(source: Observable) -> Observable: +) -> Callable[[Observable[_T]], Observable[_T]]: + def throttle_first(source: Observable[_T]) -> Observable[_T]: """Returns an observable that emits only the first item emitted by the source Observable during sequential time windows of a specifiedduration. @@ -19,27 +22,36 @@ def throttle_first(source: Observable) -> Observable: An Observable that performs the throttle operation. """ - def subscribe(observer, scheduler_=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() duration = _scheduler.to_timedelta(window_duration or 0.0) if duration <= _scheduler.to_timedelta(0): raise ValueError("window_duration cannot be less or equal zero.") - last_on_next = [0] + last_on_next: Optional[datetime] = None - def on_next(x): + def on_next(x: _T) -> None: + nonlocal last_on_next emit = False now = _scheduler.now with source.lock: - if not last_on_next[0] or now - last_on_next[0] >= duration: - last_on_next[0] = now + if not last_on_next or now - last_on_next >= duration: + last_on_next = now emit = True if emit: observer.on_next(x) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler=_scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler=_scheduler + ) return Observable(subscribe) return throttle_first + + +__all__ = ["throttle_first"] diff --git a/rx/core/operators/timeinterval.py b/rx/core/operators/timeinterval.py index c17dbd9d7..6969b1dd1 100644 --- a/rx/core/operators/timeinterval.py +++ b/rx/core/operators/timeinterval.py @@ -1,18 +1,22 @@ from datetime import timedelta -from typing import Any, Callable, NamedTuple, Optional +from typing import Any, Callable, NamedTuple, Optional, TypeVar from rx import operators as ops from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") + class TimeInterval(NamedTuple): value: Any interval: timedelta -def _time_interval(scheduler: Optional[abc.SchedulerBase] = None) -> Callable[[Observable], Observable]: - def time_interval(source: Observable) -> Observable: +def time_interval( + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def time_interval(source: Observable[_T]) -> Observable[_T]: """Records the time interval between consecutive values in an observable sequence. @@ -23,11 +27,14 @@ def time_interval(source: Observable) -> Observable: values. """ - def subscribe(observer, scheduler_): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() last = _scheduler.now - def mapper(value): + def mapper(value: _T) -> TimeInterval: nonlocal last now = _scheduler.now @@ -35,7 +42,7 @@ def mapper(value): last = now return TimeInterval(value=value, interval=span) - return source.pipe(ops.map(mapper)).subscribe(observer, scheduler_) + return source.pipe(ops.map(mapper)).subscribe(observer, _scheduler) return Observable(subscribe) diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index 616345e3a..318fb7859 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -1,28 +1,32 @@ from asyncio import Future from datetime import datetime -from typing import Callable, Optional, Union, cast +from typing import Any, Callable, Optional, TypeVar, Union from rx import from_future, throw from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) -from rx.internal.utils import is_future +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _timeout( - duetime: typing.AbsoluteTime, - other: Optional[Union[Observable, Future]] = None, + +def timeout( + duetime: typing.AbsoluteOrRelativeTime, + other: Optional[Union[Observable[_T], "Future[_T]"]] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: other = other or throw(Exception("Timeout")) - if is_future(other): - obs = from_future(cast(Future, other)) + if isinstance(other, Future): + obs = from_future(other) else: - obs = cast(Observable, other) + obs = other - def timeout(source: Observable) -> Observable: + def timeout(source: Observable[_T]) -> Observable[_T]: """Returns the source observable sequence or the other observable sequence if duetime elapses. @@ -37,7 +41,10 @@ def timeout(source: Observable) -> Observable: case of a timeout. """ - def subscribe(observer, scheduler_=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() if isinstance(duetime, datetime): @@ -56,24 +63,26 @@ def subscribe(observer, scheduler_=None): def create_timer(): my_id = _id[0] - def action(scheduler, state=None): + def action(scheduler: abc.SchedulerBase, state: Any = None): switched[0] = _id[0] == my_id timer_wins = switched[0] if timer_wins: - subscription.disposable = obs.subscribe(observer, scheduler=scheduler) + subscription.disposable = obs.subscribe( + observer, scheduler=scheduler + ) timer.disposable = scheduler_method(duetime, action) create_timer() - def on_next(value): + def on_next(value: _T) -> None: send_wins = not switched[0] if send_wins: _id[0] += 1 observer.on_next(value) create_timer() - def on_error(error): + def on_error(error: Exception) -> None: on_error_wins = not switched[0] if on_error_wins: _id[0] += 1 @@ -85,7 +94,9 @@ def on_completed(): _id[0] += 1 observer.on_completed() - original.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler_) + original.disposable = source.subscribe_( + on_next, on_error, on_completed, scheduler_ + ) return CompositeDisposable(subscription, timer) return Observable(subscribe) diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index 97402cec7..f8e453dab 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -1,19 +1,21 @@ -from typing import Any, Callable, Optional +from typing import Any, Callable, Optional, TypeVar import rx -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import ( CompositeDisposable, SerialDisposable, SingleAssignmentDisposable, ) +_T = TypeVar("_T") + def timeout_with_mapper( - first_timeout: Optional[Observable] = None, - timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, - other: Optional[Observable] = None, -) -> Callable[[Observable], Observable]: + first_timeout: Optional[Observable[_T]] = None, + timeout_duration_mapper: Optional[Callable[[Any], Observable[Any]]] = None, + other: Optional[Observable[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the source observable sequence, switching to the other observable sequence if a timeout is signaled. @@ -39,8 +41,11 @@ def timeout_with_mapper( first_timeout = first_timeout or rx.never() other = other or rx.throw(Exception("Timeout")) - def timeout_with_mapper(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def timeout_with_mapper(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: subscription = SerialDisposable() timer = SerialDisposable() original = SingleAssignmentDisposable() @@ -50,7 +55,7 @@ def subscribe(observer, scheduler=None): switched = False _id = [0] - def set_timer(timeout: Observable) -> None: + def set_timer(timeout: Observable[Any]) -> None: my_id = _id[0] def timer_wins(): @@ -59,7 +64,7 @@ def timer_wins(): d = SingleAssignmentDisposable() timer.disposable = d - def on_next(x): + def on_next(x: Any) -> None: if timer_wins(): subscription.disposable = other.subscribe( observer, scheduler=scheduler @@ -67,11 +72,11 @@ def on_next(x): d.dispose() - def on_error(e): + def on_error(e: Exception) -> None: if timer_wins(): observer.on_error(e) - def on_completed(): + def on_completed() -> None: if timer_wins(): subscription.disposable = other.subscribe(observer) @@ -88,7 +93,7 @@ def observer_wins(): return res - def on_next(x): + def on_next(x: _T) -> None: if observer_wins(): observer.on_next(x) timeout = None @@ -100,11 +105,11 @@ def on_next(x): set_timer(timeout) - def on_error(error): + def on_error(error: Exception) -> None: if observer_wins(): observer.on_error(error) - def on_completed(): + def on_completed() -> None: if observer_wins(): observer.on_completed() @@ -116,3 +121,6 @@ def on_completed(): return Observable(subscribe) return timeout_with_mapper + + +__all__ = ["timeout_with_mapper"] diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index 66ab39624..1d2172a5d 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -1,11 +1,17 @@ -from typing import Any, Callable, Optional +from typing import cast, Callable, Optional, TypeVar, Dict from rx.core import Observable, abc from rx.core.typing import Mapper +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") +_TValue = TypeVar("_TValue") -def _to_dict(key_mapper: Mapper, element_mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: - def to_dict(source: Observable) -> Observable: + +def to_dict( + key_mapper: Mapper[_T, _TKey], element_mapper: Optional[Mapper[_T, _TValue]] = None +) -> Callable[[Observable[_T]], Observable[Dict[_TKey, _TValue]]]: + def to_dict(source: Observable[_T]) -> Observable[Dict[_TKey, _TValue]]: """Converts the observable sequence to a Map if it exists. Args: @@ -16,10 +22,13 @@ def to_dict(source: Observable) -> Observable: containing the values from the observable sequence. """ - def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: - m = dict() + def subscribe( + observer: abc.ObserverBase[Dict[_TKey, _TValue]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + m: Dict[_TKey, _TValue] = dict() - def on_next(x: Any) -> None: + def on_next(x: _T) -> None: try: key = key_mapper(x) except Exception as ex: # pylint: disable=broad-except @@ -34,7 +43,7 @@ def on_next(x: Any) -> None: observer.on_error(ex) return - m[key] = element + m[key] = cast(_TValue, element) def on_completed() -> None: nonlocal m @@ -42,8 +51,13 @@ def on_completed() -> None: m = dict() observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) return Observable(subscribe) return to_dict + + +__all__ = ["to_dict"] diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index 12aee8c42..d21581a6a 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _to_future( +def to_future( future_ctor: Optional[Callable[[], "Future[_T]"]] = None, scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], "Future[_T]"]: @@ -61,4 +61,4 @@ def on_completed(): return to_future -__all__ = ["_to_future"] +__all__ = ["to_future"] diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index d871e7e7b..3db3a67d0 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -1,4 +1,4 @@ -from typing import Callable, Iterable, List, Optional, TypeVar +from typing import Callable, List, Optional, TypeVar from rx.core import Observable, abc @@ -6,8 +6,8 @@ _T = TypeVar("_T") -def _to_iterable() -> Callable[[Observable[_T]], Observable[Iterable[_T]]]: - def to_iterable(source: Observable[_T]) -> Observable[Iterable[_T]]: +def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: + def to_iterable(source: Observable[_T]) -> Observable[List[_T]]: """Creates an iterable from an observable sequence. Returns: @@ -17,7 +17,7 @@ def to_iterable(source: Observable[_T]) -> Observable[Iterable[_T]]: """ def subscribe( - observer: abc.ObserverBase[Iterable[_T]], + observer: abc.ObserverBase[List[_T]], scheduler: Optional[abc.SchedulerBase] = None, ): nonlocal source @@ -42,4 +42,4 @@ def on_completed(): return to_iterable -__all__ = ["_to_iterable"] +__all__ = ["to_iterable"] diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index 68dc7b529..2d1beace3 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Optional, Any from rx.core import Observable, abc from rx.core.typing import RelativeTime @@ -7,8 +7,10 @@ new_thread_scheduler = NewThreadScheduler() -def _to_marbles(scheduler: Optional[abc.SchedulerBase] = None, timespan: RelativeTime = 0.1): - def to_marbles(source: Observable) -> Observable: +def to_marbles( + scheduler: Optional[abc.SchedulerBase] = None, timespan: RelativeTime = 0.1 +): + def to_marbles(source: Observable[Any]) -> Observable[str]: """Convert an observable sequence into a marble diagram string. Args: @@ -21,7 +23,10 @@ def to_marbles(source: Observable) -> Observable: Observable stream. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[str], + scheduler: Optional[abc.SchedulerBase] = None, + ): scheduler = scheduler or new_thread_scheduler result: List[str] = [] @@ -37,11 +42,11 @@ def add_timespan(): dashes = "-" * int((secs + timespan / 2.0) * (1.0 / timespan)) result.append(dashes) - def on_next(value): + def on_next(value: Any) -> None: add_timespan() result.append(stringify(value)) - def on_error(exception): + def on_error(exception: Exception) -> None: add_timespan() result.append(stringify(exception)) observer.on_next("".join(n for n in result)) @@ -60,10 +65,13 @@ def on_completed(): return to_marbles -def stringify(value): +def stringify(value: Any) -> str: """Utility for stringifying an event.""" string = str(value) if len(string) > 1: string = "(%s)" % string return string + + +__all__ = ["stringify"] diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index 375a943ed..a3264773e 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def _to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: +def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: """Converts the observable sequence to a set. Returns an observable sequence with a single value of a set @@ -32,4 +32,4 @@ def on_completed() -> None: return to_set -__all__ = ["_to_set"] +__all__ = ["to_set"] diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index d3b6f7599..1ef7b3a58 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def _while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: """Repeats source as long as condition holds emulating a while loop. @@ -33,4 +33,4 @@ def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: return while_do -__all__ = ["_while_do"] +__all__ = ["while_do"] diff --git a/rx/core/operators/withlatestfrom.py b/rx/core/operators/withlatestfrom.py index 6ac80c8e3..e3e032dfa 100644 --- a/rx/core/operators/withlatestfrom.py +++ b/rx/core/operators/withlatestfrom.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Any, Callable import rx from rx.core import Observable -def _with_latest_from(*sources: Observable) -> Callable[[Observable], Observable]: +def with_latest_from( + *sources: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Any]]: """With latest from operator. Merges the specified observable sequences into one observable @@ -21,6 +23,10 @@ def _with_latest_from(*sources: Observable) -> Callable[[Observable], Observable elements of the sources into a tuple. """ - def with_latest_from(source: Observable) -> Observable: + def with_latest_from(source: Observable[Any]) -> Observable[Any]: return rx.with_latest_from(source, *sources) + return with_latest_from + + +__all__ = ["with_latest_from"] diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index 20eca8093..22487357b 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -8,7 +8,7 @@ # pylint: disable=redefined-builtin -def _zip( +def zip( *args: Observable[Any], ) -> Callable[[Observable[Any]], Observable[Tuple[Any, ...]]]: def zip(source: Observable[Any]) -> Observable[Tuple[Any, ...]]: @@ -32,7 +32,7 @@ def zip(source: Observable[Any]) -> Observable[Tuple[Any, ...]]: return zip -def _zip_with_iterable( +def zip_with_iterable( seq: Iterable[_TOther], ) -> Callable[[Observable[_T]], Observable[Tuple[_T, _TOther]]]: def zip_with_iterable(source: Observable[_T]) -> Observable[Tuple[_T, _TOther]]: @@ -81,4 +81,4 @@ def on_next(left: _T) -> None: return zip_with_iterable -__all__ = ["_zip", "_zip_with_iterable"] +__all__ = ["zip", "zip_with_iterable"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index e660d6bba..b22e4f9b5 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -4,6 +4,7 @@ from typing import ( Any, Callable, + Dict, Iterable, List, Optional, @@ -39,6 +40,7 @@ _T2 = TypeVar("_T2") _TKey = TypeVar("_TKey") _TState = TypeVar("_TState") +_TValue = TypeVar("_TValue") def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: @@ -64,9 +66,9 @@ def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool] determining whether all elements in the source sequence pass the test in the specified predicate. """ - from rx.core.operators.all import _all + from rx.core.operators.all import all - return _all(predicate) + return all(predicate) def amb(right_source: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -140,7 +142,9 @@ def average( return average(key_mapper) -def buffer(boundaries: Observable) -> Callable[[Observable], Observable]: +def buffer( + boundaries: Observable[Any], +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers. @@ -163,9 +167,9 @@ def buffer(boundaries: Observable) -> Callable[[Observable], Observable]: A function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.buffer import _buffer + from rx.core.operators.buffer import buffer - return _buffer(boundaries) + return buffer(boundaries) def buffer_when( @@ -265,9 +269,9 @@ def buffer_with_count( A function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.buffer import _buffer_with_count + from rx.core.operators.buffer import buffer_with_count - return _buffer_with_count(count, skip) + return buffer_with_count(count, skip) def buffer_with_time( @@ -527,9 +531,9 @@ def debounce( An operator function that takes the source observable and returns the debounced observable sequence. """ - from rx.core.operators.debounce import _debounce + from rx.core.operators.debounce import debounce - return _debounce(duetime, scheduler) + return debounce(duetime, scheduler) throttle_with_timeout = debounce @@ -3302,7 +3306,7 @@ def take_with_time( def throttle_first( window_duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns an Observable that emits only the first item emitted by the source Observable during sequential time windows of a specified duration. @@ -3315,14 +3319,14 @@ def throttle_first( An operator function that takes an observable source and returns an observable that performs the throttle operation. """ - from rx.core.operators.throttlefirst import _throttle_first + from rx.core.operators.throttlefirst import throttle_first - return _throttle_first(window_duration, scheduler) + return throttle_first(window_duration, scheduler) def throttle_with_mapper( - throttle_duration_mapper: Callable[[Any], Observable] -) -> Callable[[Observable], Observable]: + throttle_duration_mapper: Callable[[Any], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[_T]]: """The throttle_with_mapper operator. Ignores values from an observable sequence which are followed by @@ -3340,9 +3344,9 @@ def throttle_with_mapper( A partially applied operator function that takes an observable source and returns the throttled observable sequence. """ - from rx.core.operators.debounce import _throttle_with_mapper + from rx.core.operators.debounce import throttle_with_mapper - return _throttle_with_mapper(throttle_duration_mapper) + return throttle_with_mapper(throttle_duration_mapper) def timestamp( @@ -3369,10 +3373,10 @@ def timestamp( def timeout( - duetime: typing.AbsoluteTime, - other: Optional[Observable] = None, + duetime: typing.AbsoluteOrRelativeTime, + other: Optional[Observable[_T]] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the source observable sequence or the other observable sequence if duetime elapses. @@ -3402,16 +3406,16 @@ def timeout( returns the source sequence switching to the other sequence in case of a timeout. """ - from rx.core.operators.timeout import _timeout + from rx.core.operators.timeout import timeout - return _timeout(duetime, other, scheduler) + return timeout(duetime, other, scheduler) def timeout_with_mapper( - first_timeout: Optional[Observable] = None, - timeout_duration_mapper: Optional[Callable[[Any], Observable]] = None, - other: Optional[Observable] = None, -) -> Callable[[Observable], Observable]: + first_timeout: Optional[Observable[Any]] = None, + timeout_duration_mapper: Optional[Callable[[_T], Observable[Any]]] = None, + other: Optional[Observable[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the source observable sequence, switching to the other observable sequence if a timeout is signaled. @@ -3442,7 +3446,7 @@ def timeout_with_mapper( def time_interval( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Records the time interval between consecutive values in an observable sequence. @@ -3461,14 +3465,14 @@ def time_interval( returns an observable sequence with time interval information on values. """ - from rx.core.operators.timeinterval import _time_interval + from rx.core.operators.timeinterval import time_interval - return _time_interval(scheduler=scheduler) + return time_interval(scheduler=scheduler) def to_dict( - key_mapper: Mapper, element_mapper: Optional[Mapper] = None -) -> Callable[[Observable], Observable]: + key_mapper: Mapper[_T, _TKey], element_mapper: Optional[Mapper[_T, _TValue]] = None +) -> Callable[[Observable[_T]], Observable[Dict[_TKey, _TValue]]]: """Converts the observable sequence to a Map if it exists. Args: @@ -3483,14 +3487,14 @@ def to_dict( returns an observable sequence with a single value of a dictionary containing the values from the observable sequence. """ - from rx.core.operators.todict import _to_dict + from rx.core.operators.todict import to_dict - return _to_dict(key_mapper, element_mapper) + return to_dict(key_mapper, element_mapper) def to_future( - future_ctor: Optional[Callable[[], Future]] = None -) -> Callable[[Observable], Future]: + future_ctor: Optional[Callable[[], "Future[_T]"]] = None +) -> Callable[[Observable[_T]], "Future[_T]"]: """Converts an existing observable sequence to a Future. Example: @@ -3503,9 +3507,9 @@ def to_future( An operator function that takes an observable source and returns a future with the last value from the observable sequence. """ - from rx.core.operators.tofuture import _to_future + from rx.core.operators.tofuture import to_future - return _to_future(future_ctor) + return to_future(future_ctor) def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -3518,9 +3522,9 @@ def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: returns an observable sequence containing a single element with an iterable containing all the elements of the source sequence. """ - from rx.core.operators.toiterable import _to_iterable + from rx.core.operators.toiterable import to_iterable - return _to_iterable() + return to_iterable() to_list = to_iterable @@ -3528,7 +3532,7 @@ def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: def to_marbles( timespan: typing.RelativeTime = 0.1, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[Any]], Observable[str]]: """Convert an observable sequence into a marble diagram string. Args: @@ -3540,9 +3544,9 @@ def to_marbles( Returns: Observable stream. """ - from rx.core.operators.tomarbles import _to_marbles + from rx.core.operators.tomarbles import to_marbles - return _to_marbles(scheduler=scheduler, timespan=timespan) + return to_marbles(scheduler=scheduler, timespan=timespan) def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: @@ -3553,9 +3557,9 @@ def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: returns an observable sequence with a single value of a set containing the values from the observable sequence. """ - from rx.core.operators.toset import _to_set + from rx.core.operators.toset import to_set - return _to_set() + return to_set() def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3571,9 +3575,9 @@ def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[ returns an observable sequence which is repeated as long as the condition holds. """ - from rx.core.operators.whiledo import _while_do + from rx.core.operators.whiledo import while_do - return _while_do(condition) + return while_do(condition) def window( @@ -3738,7 +3742,9 @@ def window_with_time_or_count( return _window_with_time_or_count(timespan, count, scheduler) -def with_latest_from(*sources: Observable) -> Callable[[Observable], Observable]: +def with_latest_from( + *sources: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Any]]: """The `with_latest_from` operator. Merges the specified observable sequences into one observable @@ -3763,12 +3769,12 @@ def with_latest_from(*sources: Observable) -> Callable[[Observable], Observable] returns an observable sequence containing the result of combining elements of the sources into a tuple. """ - from rx.core.operators.withlatestfrom import _with_latest_from + from rx.core.operators.withlatestfrom import with_latest_from - return _with_latest_from(*sources) + return with_latest_from(*sources) -def zip(*args: Observable) -> Callable[[Observable], Observable]: +def zip(*args: Observable[Any]) -> Callable[[Observable[Any]], Observable[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 @@ -3794,9 +3800,9 @@ def zip(*args: Observable) -> Callable[[Observable], Observable]: returns an observable sequence containing the result of combining elements of the sources as a tuple. """ - from rx.core.operators.zip import _zip + from rx.core.operators.zip import zip - return _zip(*args) + return zip(*args) def zip_with_iterable( @@ -3825,9 +3831,9 @@ def zip_with_iterable( returns an observable sequence containing the result of combining elements of the sources as a tuple. """ - from rx.core.operators.zip import _zip_with_iterable + from rx.core.operators.zip import zip_with_iterable - return _zip_with_iterable(second) + return zip_with_iterable(second) zip_with_list = zip_with_iterable From daf2e5c5f05c460cdf41afa3ddf7f0085e5f221e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 09:43:22 +0100 Subject: [PATCH 013/103] Name operator implementations with postfix underscore - Prefix underscore is for private names inside a module --- rx/__init__.py | 150 +++++++++++------- rx/core/observable/amb.py | 4 +- rx/core/observable/case.py | 4 +- rx/core/observable/catch.py | 21 ++- rx/core/observable/combinelatest.py | 9 +- rx/core/observable/concat.py | 4 +- rx/core/observable/defer.py | 4 +- rx/core/observable/empty.py | 4 +- rx/core/observable/forkjoin.py | 13 +- rx/core/observable/fromfuture.py | 4 +- rx/core/observable/fromiterable.py | 11 +- .../observable/generatewithrelativetime.py | 4 +- rx/core/observable/ifthen.py | 8 +- rx/core/observable/interval.py | 6 +- rx/core/observable/merge.py | 4 +- rx/core/observable/never.py | 4 +- rx/core/observable/observable.py | 3 +- rx/core/observable/onerrorresumenext.py | 15 +- rx/core/observable/range.py | 13 +- rx/core/observable/repeat.py | 4 +- rx/core/observable/returnvalue.py | 4 +- rx/core/observable/start.py | 4 +- rx/core/observable/startasync.py | 4 +- rx/core/observable/throw.py | 4 +- rx/core/observable/timer.py | 4 +- rx/core/observable/toasync.py | 4 +- rx/core/observable/withlatestfrom.py | 4 +- rx/core/operators/all.py | 4 +- rx/core/operators/amb.py | 4 +- rx/core/operators/asobservable.py | 4 +- rx/core/operators/average.py | 4 +- rx/core/operators/buffer.py | 8 +- rx/core/operators/combinelatest.py | 4 +- rx/core/operators/concat.py | 4 +- rx/core/operators/connectable/refcount.py | 40 +++-- rx/core/operators/contains.py | 9 +- rx/core/operators/count.py | 11 +- rx/core/operators/defaultifempty.py | 27 +++- rx/core/operators/delay.py | 4 +- rx/core/pipe.py | 62 ++++---- rx/core/typing.py | 1 + rx/internal/utils.py | 11 +- rx/operators/__init__.py | 60 +++---- 43 files changed, 337 insertions(+), 237 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 93954448b..7ab8345fa 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -20,8 +20,18 @@ _T = TypeVar("_T") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") +_TKey = TypeVar("_TKey") _TState = TypeVar("_TState") +_A = TypeVar("_A") +_B = TypeVar("_B") +_C = TypeVar("_C") +_D = TypeVar("_D") +_E = TypeVar("_E") +_F = TypeVar("_F") +_G = TypeVar("_G") + + # Please make sure the version here remains the same as in project.cfg __version__ = "3.2.0" @@ -49,16 +59,16 @@ def amb(*sources: Observable[_T]) -> Observable[_T]: whichever emitted the first element. """ - from .core.observable.amb import _amb + from .core.observable.amb import amb as amb_ - return _amb(*sources) + return amb_(*sources) def case( - mapper: Callable[[], _T1], - sources: Mapping[_T1, _T2], - default_source: Optional[Union[Observable[_T2], Future]] = None, -) -> Observable[_T2]: + mapper: Callable[[], _TKey], + sources: Mapping[_TKey, Observable[_T]], + default_source: Optional[Union[Observable[_T], "Future[_T]"]] = None, +) -> Observable[_T]: """Uses mapper to determine which source in sources to use. .. marble:: @@ -87,9 +97,9 @@ def case( An observable sequence which is determined by a case statement. """ - from .core.observable.case import _case + from .core.observable.case import case_ - return _case(mapper, sources, default_source) + return case_(mapper, sources, default_source) def catch(*sources: Observable[_T]) -> Observable[_T]: @@ -115,9 +125,9 @@ def catch(*sources: Observable[_T]) -> Observable[_T]: from the sequence of sources until one of them terminates successfully. """ - from .core.observable.catch import _catch_with_iterable + from .core.observable.catch import catch_with_iterable_ - return _catch_with_iterable(sources) + return catch_with_iterable_(sources) def catch_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: @@ -145,9 +155,9 @@ def catch_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: from the sequence of sources until one of them terminates successfully. """ - from .core.observable.catch import _catch_with_iterable + from .core.observable.catch import catch_with_iterable_ - return _catch_with_iterable(sources) + return catch_with_iterable_(sources) def create(subscribe: typing.Subscription[_T]) -> Observable[_T]: @@ -171,7 +181,28 @@ def create(subscribe: typing.Subscription[_T]) -> Observable[_T]: return Observable(subscribe) -def combine_latest(*sources: Observable[_T]) -> Observable[_T]: +@overload +def combine_latest( + __a: Observable[_A], __b: Observable[_B] +) -> Observable[Tuple[_A, _B]]: + ... + + +@overload +def combine_latest( + __a: Observable[_A], __b: Observable[_B], __c: Observable[_C] +) -> Observable[Tuple[_A, _B, _C]]: + ... + + +@overload +def combine_latest( + __a: Observable[_A], __b: Observable[_B], __c: Observable[_C], __d: Observable[_D] +) -> Observable[Tuple[_A, _B, _C, _D]]: + ... + + +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 emits an element. @@ -195,9 +226,9 @@ def combine_latest(*sources: Observable[_T]) -> Observable[_T]: each source in given sequence. """ - from .core.observable.combinelatest import _combine_latest + from .core.observable.combinelatest import combine_latest_ - return _combine_latest(*sources) + return combine_latest_(*sources) def concat(*sources: Observable[_T]) -> Observable[_T]: @@ -222,9 +253,9 @@ def concat(*sources: Observable[_T]) -> Observable[_T]: the given sequence, in sequential order. """ - from .core.observable.concat import _concat_with_iterable + from .core.observable.concat import concat_with_iterable_ - return _concat_with_iterable(sources) + return concat_with_iterable_(sources) def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: @@ -251,9 +282,9 @@ def concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: sequence, in sequential order. """ - from .core.observable.concat import _concat_with_iterable + from .core.observable.concat import concat_with_iterable_ - return _concat_with_iterable(sources) + return concat_with_iterable_(sources) def defer( @@ -282,9 +313,9 @@ def defer( of the given factory function. """ - from .core.observable.defer import _defer + from .core.observable.defer import defer_ - return _defer(factory) + return defer_(factory) def empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: @@ -308,9 +339,9 @@ def empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: An observable sequence with no elements. """ - from .core.observable.empty import _empty + from .core.observable.empty import empty_ - return _empty(scheduler) + return empty_(scheduler) def for_in(values: Iterable[_T1], mapper: typing.Mapper[_T1, _T2]) -> Observable[_T2]: @@ -370,9 +401,9 @@ def fork_join(*sources: Observable[_T]) -> Observable[_T]: each source in given sequence. """ - from .core.observable.forkjoin import _fork_join + from .core.observable.forkjoin import fork_join_ - return _fork_join(*sources) + return fork_join_(*sources) def from_callable( @@ -447,9 +478,9 @@ def from_future(future: "Future[_T]") -> Observable[_T]: An observable sequence which wraps the existing future success and failure. """ - from .core.observable.fromfuture import _from_future + from .core.observable.fromfuture import from_future_ - return _from_future(future) + return from_future_(future) def from_iterable( @@ -477,7 +508,7 @@ def from_iterable( The observable sequence whose elements are pulled from the given iterable sequence. """ - from .core.observable.fromiterable import from_iterable as from_iterable_ + from .core.observable.fromiterable import from_iterable_ as from_iterable_ return from_iterable_(iterable, scheduler) @@ -593,9 +624,9 @@ def generate_with_relative_time( Returns: The generated sequence. """ - from .core.observable.generatewithrelativetime import _generate_with_relative_time + from .core.observable.generatewithrelativetime import generate_with_relative_time_ - return _generate_with_relative_time(initial_state, condition, iterate, time_mapper) + return generate_with_relative_time_(initial_state, condition, iterate, time_mapper) def generate( @@ -736,9 +767,9 @@ def if_then( An observable sequence which is either the then_source or else_source. """ - from .core.observable.ifthen import _if_then + from .core.observable.ifthen import if_then_ - return _if_then(condition, then_source, else_source) + return if_then_(condition, then_source, else_source) def interval( @@ -766,9 +797,9 @@ def interval( Returns: An observable sequence that produces a value after each period. """ - from .core.observable.interval import _interval + from .core.observable.interval import interval_ - return _interval(period, scheduler) + return interval_(period, scheduler) def merge(*sources: Observable[Any]) -> Observable[Any]: @@ -792,9 +823,9 @@ def merge(*sources: Observable[Any]) -> Observable[Any]: The observable sequence that merges the elements of the observable sequences. """ - from .core.observable.merge import _merge + from .core.observable.merge import merge_ - return _merge(*sources) + return merge_(*sources) def never() -> Observable[Any]: @@ -810,9 +841,9 @@ def never() -> Observable[Any]: Returns: An observable sequence whose observers will never get called. """ - from .core.observable.never import _never + from .core.observable.never import never_ - return _never() + return never_() def of(*args: _T) -> Observable[_T]: @@ -843,7 +874,7 @@ def of(*args: _T) -> Observable[_T]: def on_error_resume_next( - *sources: Union[Observable[_T], "Future[_T]"] + *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. @@ -868,9 +899,9 @@ def on_error_resume_next( An observable sequence that concatenates the source sequences, even if a sequence terminates with an exception. """ - from .core.observable.onerrorresumenext import _on_error_resume_next + from .core.observable.onerrorresumenext import on_error_resume_next_ - return _on_error_resume_next(*sources) + return on_error_resume_next_(*sources) def range( @@ -905,9 +936,9 @@ def range( An observable sequence that contains a range of sequential integral numbers. """ - from .core.observable.range import _range + from .core.observable.range import range_ - return _range(start, stop, step, scheduler) + return range_(start, stop, step, scheduler) def return_value( @@ -933,9 +964,9 @@ def return_value( Returns: An observable sequence containing the single specified element. """ - from .core.observable.returnvalue import _return_value + from .core.observable.returnvalue import return_value_ - return _return_value(value, scheduler) + return return_value_(value, scheduler) just = alias("just", "Alias for :func:`rx.return_value`.", return_value) @@ -966,9 +997,9 @@ def repeat_value( An observable sequence that repeats the given element the specified number of times. """ - from .core.observable.repeat import _repeat_value + from .core.observable.repeat import repeat_value_ - return _repeat_value(value, repeat_count) + return repeat_value_(value, repeat_count) def start( @@ -1003,9 +1034,9 @@ def start( An observable sequence exposing the function's result value, or an exception. """ - from .core.observable.start import start + from .core.observable.start import start_ - return start(func, scheduler) + return start_(func, scheduler) def start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: @@ -1026,9 +1057,9 @@ def start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: An observable sequence exposing the function's result value, or an exception. """ - from .core.observable.startasync import _start_async + from .core.observable.startasync import start_async_ - return _start_async(function_async) + return start_async_(function_async) def throw( @@ -1056,9 +1087,9 @@ def throw( The observable sequence that terminates exceptionally with the specified exception object. """ - from .core.observable.throw import _throw + from .core.observable.throw import throw_ - return _throw(exception, scheduler) + return throw_(exception, scheduler) def timer( @@ -1096,9 +1127,9 @@ def timer( An observable sequence that produces a value after due time has elapsed and then each period. """ - from .core.observable.timer import _timer + from .core.observable.timer import timer_ - return _timer(duetime, period, scheduler) + return timer_(duetime, period, scheduler) def to_async( @@ -1129,9 +1160,9 @@ def to_async( Returns: Asynchronous function. """ - from .core.observable.toasync import to_async + from .core.observable.toasync import to_async_ - return to_async(func, scheduler) + return to_async_(func, scheduler) def using( @@ -1183,9 +1214,9 @@ def with_latest_from(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: An observable sequence containing the result of combining elements of the sources into a :class:`tuple`. """ - from .core.observable.withlatestfrom import _with_latest_from + from .core.observable.withlatestfrom import with_latest_from_ - return _with_latest_from(*sources) + return with_latest_from_(*sources) def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: @@ -1236,6 +1267,7 @@ def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: "never", "on_error_resume_next", "of", + "Observable", "return_value", "pipe", "range", diff --git a/rx/core/observable/amb.py b/rx/core/observable/amb.py index 7932be675..602a14983 100644 --- a/rx/core/observable/amb.py +++ b/rx/core/observable/amb.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _amb(*sources: Observable[_T]) -> Observable[_T]: +def amb(*sources: Observable[_T]) -> Observable[_T]: """Propagates the observable sequence that reacts first. Example: @@ -29,4 +29,4 @@ def func(previous: Observable[_T], current: Observable[_T]): return acc -__all__ = ["_amb"] +__all__ = ["amb"] diff --git a/rx/core/observable/case.py b/rx/core/observable/case.py index af9cf962c..620c1a6d2 100644 --- a/rx/core/observable/case.py +++ b/rx/core/observable/case.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _case( +def case_( mapper: Callable[[], _Key], sources: Mapping[_Key, Observable[_T]], default_source: Optional[Union[Observable[_T], "Future[_T]"]] = None, @@ -33,4 +33,4 @@ def factory(_: abc.SchedulerBase) -> Observable[_T]: return defer(factory) -__all__ = ["_case"] +__all__ = ["case_"] diff --git a/rx/core/observable/catch.py b/rx/core/observable/catch.py index 4908217d9..78e1aad67 100644 --- a/rx/core/observable/catch.py +++ b/rx/core/observable/catch.py @@ -1,14 +1,18 @@ from typing import Any, Iterable, Optional, TypeVar from rx.core import Observable, abc -from rx.disposable import (CompositeDisposable, Disposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + Disposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.scheduler import CurrentThreadScheduler _T = TypeVar("_T") -def _catch_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_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. @@ -28,7 +32,9 @@ def _catch_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_T]: sources_ = iter(sources) - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler_ or CurrentThreadScheduler.singleton() subscription = SerialDisposable() @@ -57,7 +63,9 @@ def on_error(exn: Exception) -> None: else: d = SingleAssignmentDisposable() subscription.disposable = d - d.disposable = current.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler_) + d.disposable = current.subscribe_( + observer.on_next, on_error, observer.on_completed, scheduler_ + ) cancelable.disposable = _scheduler.schedule(action) @@ -68,3 +76,6 @@ def dispose(): return CompositeDisposable(subscription, cancelable, Disposable(dispose)) return Observable(subscribe) + + +__all__ = ["catch_with_iterable_"] diff --git a/rx/core/observable/combinelatest.py b/rx/core/observable/combinelatest.py index 54aaaf2a6..2848035c5 100644 --- a/rx/core/observable/combinelatest.py +++ b/rx/core/observable/combinelatest.py @@ -4,7 +4,7 @@ from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -def _combine_latest(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: +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. @@ -62,10 +62,15 @@ def on_completed(): subscription = subscriptions[i] assert subscription - subscription.disposable = sources[i].subscribe_(on_next, observer.on_error, on_completed, scheduler) + subscription.disposable = sources[i].subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) for idx in range(n): func(idx) return CompositeDisposable(subscriptions) return Observable(subscribe) + + +__all__ = ["combine_latest_"] diff --git a/rx/core/observable/concat.py b/rx/core/observable/concat.py index e0e893b9c..a4e4b8971 100644 --- a/rx/core/observable/concat.py +++ b/rx/core/observable/concat.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _concat_with_iterable(sources: Iterable[Observable[_T]]) -> Observable[_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() @@ -48,4 +48,4 @@ def dispose(): return Observable(subscribe) -__all__ = ["_concat_with_iterable"] +__all__ = ["concat_with_iterable_"] diff --git a/rx/core/observable/defer.py b/rx/core/observable/defer.py index ce1aab178..3c299b64a 100644 --- a/rx/core/observable/defer.py +++ b/rx/core/observable/defer.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _defer( +def defer_( factory: Callable[[abc.SchedulerBase], Union[Observable[_T], "Future[_T]"]] ) -> Observable[_T]: """Returns an observable sequence that invokes the specified factory @@ -40,4 +40,4 @@ def subscribe( return Observable(subscribe) -__all__ = ["_defer"] +__all__ = ["defer_"] diff --git a/rx/core/observable/empty.py b/rx/core/observable/empty.py index 48fb685d6..7b18e1da3 100644 --- a/rx/core/observable/empty.py +++ b/rx/core/observable/empty.py @@ -4,7 +4,7 @@ from rx.scheduler import ImmediateScheduler -def _empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: +def empty_(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: def subscribe( observer: abc.ObserverBase[Any], scheduler_: Optional[abc.SchedulerBase] = None ) -> abc.DisposableBase: @@ -19,4 +19,4 @@ def action(_: abc.SchedulerBase, __: Any) -> None: return Observable(subscribe) -__all__ = ["_empty"] +__all__ = ["empty_"] diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index 6946e3378..b3e6ab50f 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -4,7 +4,7 @@ from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -def _fork_join(*sources: Observable) -> Observable: +def fork_join_(*sources: Observable) -> Observable: """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. @@ -19,7 +19,9 @@ def _fork_join(*sources: Observable) -> Observable: parent = sources[0] - def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> CompositeDisposable: + def subscribe( + observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None + ) -> CompositeDisposable: n = len(sources) values = [None] * n is_done = [False] * n @@ -53,10 +55,15 @@ def on_completed(): with parent.lock: done(i) - subscriptions[i].disposable = sources[i].subscribe_(on_next, observer.on_error, on_completed, scheduler) + subscriptions[i].disposable = sources[i].subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) for i in range(n): _subscribe(i) return CompositeDisposable(subscriptions) return Observable(subscribe) + + +__all__ = ["fork_join_"] diff --git a/rx/core/observable/fromfuture.py b/rx/core/observable/fromfuture.py index 58b3e6827..b39887f69 100644 --- a/rx/core/observable/fromfuture.py +++ b/rx/core/observable/fromfuture.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _from_future(future: "Future[_T]") -> Observable[_T]: +def from_future_(future: "Future[_T]") -> Observable[_T]: """Converts a Future to an Observable sequence Args: @@ -47,4 +47,4 @@ def dispose() -> None: return Observable(subscribe) -__all__ = ["_from_future"] +__all__ = ["from_future_"] diff --git a/rx/core/observable/fromiterable.py b/rx/core/observable/fromiterable.py index f8ac42bf0..d6bedd934 100644 --- a/rx/core/observable/fromiterable.py +++ b/rx/core/observable/fromiterable.py @@ -7,7 +7,9 @@ _T = TypeVar("_T") -def from_iterable(iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_T]: +def from_iterable_( + iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[_T]: """Converts an iterable to an observable sequence. Example: @@ -22,7 +24,9 @@ def from_iterable(iterable: Iterable[_T], scheduler: Optional[abc.SchedulerBase] given iterable sequence. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + 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 @@ -47,3 +51,6 @@ def dispose() -> None: return CompositeDisposable(_scheduler.schedule(action), disp) return Observable(subscribe) + + +__all__ = ["from_iterable_"] diff --git a/rx/core/observable/generatewithrelativetime.py b/rx/core/observable/generatewithrelativetime.py index d6754fa36..dd7f8d2f1 100644 --- a/rx/core/observable/generatewithrelativetime.py +++ b/rx/core/observable/generatewithrelativetime.py @@ -8,7 +8,7 @@ _TState = TypeVar("_TState") -def _generate_with_relative_time( +def generate_with_relative_time_( initial_state: _TState, condition: Predicate[_TState], iterate: Mapper[_TState, _TState], @@ -85,4 +85,4 @@ def action(scheduler: abc.SchedulerBase, _: Any): return Observable(subscribe) -__all__ = ["_generate_with_relative_time"] +__all__ = ["generate_with_relative_time_"] diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index db59a74a4..bfb3dcaeb 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -1,13 +1,13 @@ from asyncio import Future -from typing import Callable, Union, cast, TypeVar +from typing import Callable, Union, TypeVar import rx -from rx.core import Observable, abc, typing +from rx.core import Observable, abc _T = TypeVar("_T") -def _if_then( +def if_then_( condition: Callable[[], bool], then_source: Union[Observable[_T], "Future[_T]"], else_source: Union[None, Observable[_T], "Future[_T]"] = None, @@ -48,4 +48,4 @@ def factory(_: abc.SchedulerBase) -> Observable[_T]: return rx.defer(factory) -__all__ = ["_if_then"] +__all__ = ["if_then_"] diff --git a/rx/core/observable/interval.py b/rx/core/observable/interval.py index 0a2576caa..2f2f89795 100644 --- a/rx/core/observable/interval.py +++ b/rx/core/observable/interval.py @@ -4,9 +4,11 @@ from rx.core import Observable, abc, typing -def _interval(period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None) -> Observable[int]: +def interval_( + period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[int]: return timer(period, period, scheduler) -__all__ = ["_interval"] +__all__ = ["interval_"] diff --git a/rx/core/observable/merge.py b/rx/core/observable/merge.py index 62c39aac6..fdddc79fc 100644 --- a/rx/core/observable/merge.py +++ b/rx/core/observable/merge.py @@ -6,8 +6,8 @@ _T = TypeVar("_T") -def _merge(*sources: Observable[_T]) -> Observable[_T]: +def merge_(*sources: Observable[_T]) -> Observable[_T]: return rx.from_iterable(sources).pipe(ops.merge_all()) -__all__ = ["_merge"] +__all__ = ["merge_"] diff --git a/rx/core/observable/never.py b/rx/core/observable/never.py index a802b26f4..c514d6d41 100644 --- a/rx/core/observable/never.py +++ b/rx/core/observable/never.py @@ -4,7 +4,7 @@ from rx.disposable import Disposable -def _never() -> Observable[Any]: +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). @@ -18,4 +18,4 @@ def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.Scheduler return Observable(subscribe) -__all__ = ["_never"] +__all__ = ["never_"] diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index bc275c519..b9228c8c8 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -162,7 +162,8 @@ def fix_subscriber( if isinstance(subscriber, abc.DisposableBase) or hasattr( subscriber, "dispose" ): - return subscriber + # Note: cast can be avoided using Protocols (Python 3.9) + return cast(abc.DisposableBase, subscriber) return Disposable(subscriber) diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 88b4419f0..0c2c66960 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Optional, Union, TypeVar, Any +from typing import Callable, Optional, Union, TypeVar, Any import rx from rx.core import Observable, abc @@ -13,8 +13,10 @@ _T = TypeVar("_T") -def _on_error_resume_next( - *sources: Union[Observable[_T], "Future[_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. @@ -32,13 +34,12 @@ def _on_error_resume_next( def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None ): - scheduler = scheduler or CurrentThreadScheduler.singleton() subscription = SerialDisposable() cancelable = SerialDisposable() - def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): + def action(scheduler: abc.SchedulerBase, state: Optional[Exception] = None): try: source = next(sources_) except StopIteration: @@ -52,7 +53,7 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): d = SingleAssignmentDisposable() subscription.disposable = d - def on_resume(state=None): + def on_resume(state: Optional[Exception] = None): scheduler.schedule(action, state) d.disposable = current.subscribe_( @@ -65,4 +66,4 @@ def on_resume(state=None): return Observable(subscribe) -__all__ = ["_on_error_resume_next"] +__all__ = ["on_error_resume_next_"] diff --git a/rx/core/observable/range.py b/rx/core/observable/range.py index bf43fa9f2..80dcc70c7 100644 --- a/rx/core/observable/range.py +++ b/rx/core/observable/range.py @@ -6,8 +6,11 @@ from rx.scheduler import CurrentThreadScheduler -def _range( - start: int, stop: Optional[int] = None, step: Optional[int] = None, scheduler: Optional[abc.SchedulerBase] = None +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 @@ -38,7 +41,9 @@ def _range( else: range_t = range(start, _stop, _step) - def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): nonlocal range_t _scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton() @@ -58,4 +63,4 @@ def action(scheduler: abc.SchedulerBase, iterator: Optional[Iterator[int]]): return Observable(subscribe) -__all__ = ["_range"] +__all__ = ["range_"] diff --git a/rx/core/observable/repeat.py b/rx/core/observable/repeat.py index 0051fc8c8..cd26e1766 100644 --- a/rx/core/observable/repeat.py +++ b/rx/core/observable/repeat.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _repeat_value( +def repeat_value_( value: _T = None, repeat_count: Optional[int] = None ) -> Observable[_T]: """Generates an observable sequence that repeats the given element @@ -34,4 +34,4 @@ def _repeat_value( return xs.pipe(ops.repeat(repeat_count)) -__all__ = ["_repeat_value"] +__all__ = ["repeat_value_"] diff --git a/rx/core/observable/returnvalue.py b/rx/core/observable/returnvalue.py index b44cb727c..7d4649bf6 100644 --- a/rx/core/observable/returnvalue.py +++ b/rx/core/observable/returnvalue.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _return_value(value: _T, scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_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'. @@ -53,4 +53,4 @@ def action(_: abc.SchedulerBase, __: Any = None): return Observable(subscribe) -__all__ = ["_return_value", "_from_callable"] +__all__ = ["return_value_", "_from_callable"] diff --git a/rx/core/observable/start.py b/rx/core/observable/start.py index 41d885a77..4c16e9544 100644 --- a/rx/core/observable/start.py +++ b/rx/core/observable/start.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def start( +def start_( func: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None ) -> Observable[_T]: """Invokes the specified function asynchronously on the specified @@ -34,4 +34,4 @@ def start( return to_async(func, scheduler)() -__all__ = ["start"] +__all__ = ["start_"] diff --git a/rx/core/observable/startasync.py b/rx/core/observable/startasync.py index 2ae9dc0aa..de461dca8 100644 --- a/rx/core/observable/startasync.py +++ b/rx/core/observable/startasync.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: +def start_async_(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: try: future = function_async() except Exception as ex: # pylint: disable=broad-except @@ -16,4 +16,4 @@ def _start_async(function_async: Callable[[], "Future[_T]"]) -> Observable[_T]: return from_future(future) -__all__ = ["_start_async"] +__all__ = ["start_async_"] diff --git a/rx/core/observable/throw.py b/rx/core/observable/throw.py index cc7de16b9..e9ad9e1b4 100644 --- a/rx/core/observable/throw.py +++ b/rx/core/observable/throw.py @@ -4,7 +4,7 @@ from rx.scheduler import ImmediateScheduler -def _throw( +def throw_( exception: Union[str, Exception], scheduler: Optional[abc.SchedulerBase] = None ) -> Observable[Any]: exception = exception if isinstance(exception, Exception) else Exception(exception) @@ -22,4 +22,4 @@ def action(scheduler: abc.SchedulerBase, state: Any): return Observable(subscribe) -__all__ = ["_throw"] +__all__ = ["throw_"] diff --git a/rx/core/observable/timer.py b/rx/core/observable/timer.py index 8081ea6d4..d6906810e 100644 --- a/rx/core/observable/timer.py +++ b/rx/core/observable/timer.py @@ -94,7 +94,7 @@ def action(count: int): return observable_timer_duetime_and_period(duetime, period, scheduler) -def _timer( +def timer_( duetime: typing.AbsoluteOrRelativeTime, period: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, @@ -111,4 +111,4 @@ def _timer( return observable_timer_timespan_and_period(duetime, period, scheduler) -__all__ = ["_timer"] +__all__ = ["timer_"] diff --git a/rx/core/observable/toasync.py b/rx/core/observable/toasync.py index c47db5017..c78fd0e02 100644 --- a/rx/core/observable/toasync.py +++ b/rx/core/observable/toasync.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def to_async( +def to_async_( func: Callable[..., _T], scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[..., Observable[_T]]: """Converts the function into an asynchronous function. Each @@ -51,4 +51,4 @@ def action(scheduler: abc.SchedulerBase, state: Any = None) -> None: return wrapper -__all__ = ["to_async"] +__all__ = ["to_async_"] diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index 1f348e1ae..47ae464e8 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -4,7 +4,7 @@ from rx.internal.utils import NotSet -def _with_latest_from(parent: Observable[Any], *sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: +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: @@ -42,4 +42,4 @@ def on_next(value: Any) -> None: return Observable(subscribe) -__all__ = ["_with_latest_from"] +__all__ = ["with_latest_from_"] diff --git a/rx/core/operators/all.py b/rx/core/operators/all.py index 734bec689..8e87f8e3c 100644 --- a/rx/core/operators/all.py +++ b/rx/core/operators/all.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: +def all_(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: filtering = ops.filter(lambda v: not predicate(v)) mapping = ops.map(lambda b: not b) @@ -16,4 +16,4 @@ def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool] return pipe(filtering, some, mapping) -__all__ = ["all"] +__all__ = ["all_"] diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 1bd187a2c..fbd647818 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def amb( +def amb_( right_source: Union[Observable[_T], "Future[_T]"] ) -> Callable[[Observable[_T]], Observable[_T]]: @@ -90,4 +90,4 @@ def on_completed_right() -> None: return amb -__all__ = ["amb"] +__all__ = ["amb_"] diff --git a/rx/core/operators/asobservable.py b/rx/core/operators/asobservable.py index cb37782da..0c0b58083 100644 --- a/rx/core/operators/asobservable.py +++ b/rx/core/operators/asobservable.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: +def as_observable_() -> Callable[[Observable[_T]], Observable[_T]]: def as_observable(source: Observable[_T]) -> Observable[_T]: """Hides the identity of an observable sequence. @@ -28,4 +28,4 @@ def subscribe( return as_observable -__all__ = ["as_observable"] +__all__ = ["as_observable_"] diff --git a/rx/core/operators/average.py b/rx/core/operators/average.py index 273f253ba..6c0a1d343 100644 --- a/rx/core/operators/average.py +++ b/rx/core/operators/average.py @@ -14,7 +14,7 @@ def __init__(self, sum: float, count: int): self.count = count -def average( +def average_( key_mapper: Optional[Mapper[_T, _TKey]] = None, ) -> Callable[[Observable[_T]], Observable[float]]: def average(source: Observable[_T]) -> Observable[float]: @@ -60,4 +60,4 @@ def mapper(s: AverageValue) -> float: return average -__all__ = ["average"] +__all__ = ["average_"] diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index 0583ca4d0..ebe0ca966 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def buffer( +def buffer_( boundaries: Observable[Any], ) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( @@ -32,9 +32,9 @@ def _buffer_toggle( ) -def buffer_with_count( +def buffer_with_count_( count: int, skip: Optional[int] = None -) -> Callable[[Observable[_T]], Observable[_T]]: +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on element count information. @@ -74,4 +74,4 @@ def predicate(value: List[_T]) -> bool: return buffer_with_count -__all__ = ["buffer", "buffer_with_count"] +__all__ = ["buffer_", "buffer_with_count_"] diff --git a/rx/core/operators/combinelatest.py b/rx/core/operators/combinelatest.py index b4662fdcf..29ca5a581 100644 --- a/rx/core/operators/combinelatest.py +++ b/rx/core/operators/combinelatest.py @@ -4,7 +4,7 @@ from rx.core import Observable -def _combine_latest( +def combine_latest_( *others: Observable[Any], ) -> Callable[[Observable[Any]], Observable[Any]]: def combine_latest(source: Observable[Any]) -> Observable[Any]: @@ -27,4 +27,4 @@ def combine_latest(source: Observable[Any]) -> Observable[Any]: return combine_latest -__all__ = ["_combine_latest"] +__all__ = ["combine_latest_"] diff --git a/rx/core/operators/concat.py b/rx/core/operators/concat.py index 45f3ed7d5..56f2df9f9 100644 --- a/rx/core/operators/concat.py +++ b/rx/core/operators/concat.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def _concat(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def concat_(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: def concat(source: Observable[_T]) -> Observable[_T]: """Concatenates all the observable sequences. @@ -23,4 +23,4 @@ def concat(source: Observable[_T]) -> Observable[_T]: return concat -__all__ = ["_concat"] +__all__ = ["concat_"] diff --git a/rx/core/operators/connectable/refcount.py b/rx/core/operators/connectable/refcount.py index 08540c508..7ca33bcc0 100644 --- a/rx/core/operators/connectable/refcount.py +++ b/rx/core/operators/connectable/refcount.py @@ -1,34 +1,46 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar -from rx.core import ConnectableObservable, Observable +from rx.core import ConnectableObservable, Observable, abc from rx.disposable import Disposable +_T = TypeVar("_T") -def _ref_count() -> Callable[[ConnectableObservable], Observable]: + +def ref_count_() -> Callable[[ConnectableObservable[_T]], Observable[_T]]: """Returns an observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence. """ - connectable_subscription = [None] - count = [0] + connectable_subscription: Optional[abc.DisposableBase] = None + count = 0 + + def ref_count(source: ConnectableObservable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + nonlocal connectable_subscription, count - def ref_count(source: ConnectableObservable) -> Observable: - def subscribe(observer, scheduler=None): - count[0] += 1 - should_connect = count[0] == 1 + count += 1 + should_connect = count == 1 subscription = source.subscribe(observer, scheduler=scheduler) if should_connect: - connectable_subscription[0] = source.connect(scheduler) + connectable_subscription = source.connect(scheduler) def dispose(): + nonlocal connectable_subscription, count + subscription.dispose() - count[0] -= 1 - if not count[0]: - connectable_subscription[0].dispose() + count -= 1 + if not count and connectable_subscription: + connectable_subscription.dispose() return Disposable(dispose) return Observable(subscribe) - return ref_count \ No newline at end of file + return ref_count + + +__all__ = ["ref_count_"] diff --git a/rx/core/operators/contains.py b/rx/core/operators/contains.py index 540dd1993..7d2bbe522 100644 --- a/rx/core/operators/contains.py +++ b/rx/core/operators/contains.py @@ -1,15 +1,14 @@ from typing import TypeVar, Callable, Optional from rx import operators as ops -from rx.core import Observable, pipe -from rx.core.typing import Comparer +from rx.core import Observable, pipe, typing from rx.internal.basic import default_comparer _T = TypeVar("_T") -def _contains( - value: _T, comparer: Optional[Comparer[_T]] = None +def contains_( + value: _T, comparer: Optional[typing.Comparer[_T]] = None ) -> Callable[[Observable[_T]], Observable[bool]]: comparer_ = comparer or default_comparer @@ -19,4 +18,4 @@ def _contains( return pipe(filtering, something) -__all__ = ["_contains"] +__all__ = ["contains_"] diff --git a/rx/core/operators/count.py b/rx/core/operators/count.py index 29cb45e12..04e7c1457 100644 --- a/rx/core/operators/count.py +++ b/rx/core/operators/count.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _count( +def count_( predicate: Optional[Predicate[_T]] = None, ) -> Callable[[Observable[_T]], Observable[int]]: @@ -15,8 +15,11 @@ def _count( filtering = ops.filter(predicate) return pipe(filtering, ops.count()) - counter = ops.reduce(lambda n, _: n + 1, seed=0) - return pipe(counter) + def reducer(n: int, _: _T) -> int: + return n + 1 + counter = ops.reduce(reducer, seed=0) + return counter -__all__ = ["_count"] + +__all__ = ["count_"] diff --git a/rx/core/operators/defaultifempty.py b/rx/core/operators/defaultifempty.py index 3e6ac9436..1c5e56463 100644 --- a/rx/core/operators/defaultifempty.py +++ b/rx/core/operators/defaultifempty.py @@ -1,11 +1,14 @@ -from typing import Any, Callable +from typing import Any, Callable, Optional, TypeVar -from rx.core import Observable -from rx.core.abc import DisposableBase +from rx.core import Observable, abc +_T = TypeVar("_T") -def _default_if_empty(default_value: Any = None) -> Callable[[Observable], Observable]: - def default_if_empty(source: Observable) -> Observable: + +def default_if_empty_( + default_value: _T = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def default_if_empty(source: Observable[_T]) -> Observable[_T]: """Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty. @@ -22,10 +25,13 @@ def default_if_empty(source: Observable) -> Observable: source. """ - def subscribe(observer, scheduler=None) -> DisposableBase: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: found = [False] - def on_next(x: Any): + def on_next(x: _T): found[0] = True observer.on_next(x) @@ -34,8 +40,13 @@ def on_completed(): observer.on_next(default_value) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) return Observable(subscribe) return default_if_empty + + +__all__ = ["default_if_empty_"] diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 251536caa..4f62c95cc 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -101,7 +101,7 @@ def action(scheduler, state): return Observable(subscribe) -def _delay( +def delay_( duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def delay(source: Observable[_T]) -> Observable[_T]: @@ -123,4 +123,4 @@ def delay(source: Observable[_T]) -> Observable[_T]: return delay -__all__ = ["_delay"] +__all__ = ["delay_"] diff --git a/rx/core/pipe.py b/rx/core/pipe.py index ed81cde3f..12b5f6daa 100644 --- a/rx/core/pipe.py +++ b/rx/core/pipe.py @@ -1,64 +1,64 @@ from functools import reduce from typing import Any, Callable, TypeVar, overload -A = TypeVar("A") -B = TypeVar("B") -C = TypeVar("C") -D = TypeVar("D") -E = TypeVar("E") -F = TypeVar("F") -G = TypeVar("G") +_A = TypeVar("_A") +_B = TypeVar("_B") +_C = TypeVar("_C") +_D = TypeVar("_D") +_E = TypeVar("_E") +_F = TypeVar("_F") +_G = TypeVar("_G") @overload -def pipe(__op1: Callable[[A], B]) -> Callable[[A], B]: +def pipe(__op1: Callable[[_A], _B]) -> Callable[[_A], _B]: ... @overload -def pipe(__op1: Callable[[A], B], __op2: Callable[[B], C]) -> Callable[[A], C]: +def pipe(__op1: Callable[[_A], _B], __op2: Callable[[_B], _C]) -> Callable[[_A], _C]: ... @overload def pipe( - __op1: Callable[[A], B], - __op2: Callable[[B], C], - __op3: Callable[[C], D], -) -> Callable[[A], D]: + __op1: Callable[[_A], _B], + __op2: Callable[[_B], _C], + __op3: Callable[[_C], _D], +) -> Callable[[_A], _D]: ... @overload def pipe( - __op1: Callable[[A], B], - __op2: Callable[[B], C], - __op3: Callable[[C], D], - __op4: Callable[[D], E], -) -> Callable[[A], E]: + __op1: Callable[[_A], _B], + __op2: Callable[[_B], _C], + __op3: Callable[[_C], _D], + __op4: Callable[[_D], _E], +) -> Callable[[_A], _E]: ... @overload def pipe( - __op1: Callable[[A], B], - __op2: Callable[[B], C], - __op3: Callable[[C], D], - __op4: Callable[[D], E], - __op5: Callable[[E], F], -) -> Callable[[A], F]: + __op1: Callable[[_A], _B], + __op2: Callable[[_B], _C], + __op3: Callable[[_C], _D], + __op4: Callable[[_D], _E], + __op5: Callable[[_E], _F], +) -> Callable[[_A], _F]: ... @overload def pipe( - __op1: Callable[[A], B], - __op2: Callable[[B], C], - __op3: Callable[[C], D], - __op4: Callable[[D], E], - __op5: Callable[[E], F], - __op6: Callable[[F], G], -) -> Callable[[A], G]: + __op1: Callable[[_A], _B], + __op2: Callable[[_B], _C], + __op3: Callable[[_C], _D], + __op4: Callable[[_D], _E], + __op5: Callable[[_E], _F], + __op6: Callable[[_F], _G], +) -> Callable[[_A], _G]: ... diff --git a/rx/core/typing.py b/rx/core/typing.py index 8e489d7c8..8cd7e312f 100644 --- a/rx/core/typing.py +++ b/rx/core/typing.py @@ -38,6 +38,7 @@ "Accumulator", "AbsoluteTime", "AbsoluteOrRelativeTime", + "Comparer", "Mapper", "MapperIndexed", "OnNext", diff --git a/rx/internal/utils.py b/rx/internal/utils.py index e13dd70a9..fa40aedcc 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -41,11 +41,12 @@ def alias(name: str, doc: str, fun: Callable[..., Any]) -> Callable[..., Any]: _fun = cast(FunctionType, fun) args = (_fun.__code__, _fun.__globals__) kwargs = {"name": name, "argdefs": _fun.__defaults__, "closure": _fun.__closure__} - alias = FunctionType(*args, **kwargs) # type: ignore - alias = update_wrapper(alias, _fun) - alias.__kwdefaults__ = _fun.__kwdefaults__ - alias.__doc__ = doc - return alias + alias_ = FunctionType(*args, **kwargs) # type: ignore + alias_ = update_wrapper(alias_, _fun) + alias_.__kwdefaults__ = _fun.__kwdefaults__ + alias_.__doc__ = doc + alias_.__annotations__ = _fun.__annotations__ + return alias_ class NotSet: diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index b22e4f9b5..dd20911b0 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -66,9 +66,9 @@ def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool] determining whether all elements in the source sequence pass the test in the specified predicate. """ - from rx.core.operators.all import all + from rx.core.operators.all import all_ - return all(predicate) + return all_(predicate) def amb(right_source: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -91,9 +91,9 @@ def amb(right_source: Observable[_T]) -> Callable[[Observable[_T]], Observable[_ returns an observable sequence that surfaces any of the given sequences, whichever reacted first. """ - from rx.core.operators.amb import amb + from rx.core.operators.amb import amb_ - return amb(right_source) + return amb_(right_source) def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: @@ -104,9 +104,9 @@ def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: returns and observable sequence that hides the identity of the source sequence. """ - from rx.core.operators.asobservable import as_observable + from rx.core.operators.asobservable import as_observable_ - return as_observable() + return as_observable_() def average( @@ -137,9 +137,9 @@ def average( returns an observable sequence containing a single element with the average of the sequence of values. """ - from rx.core.operators.average import average + from rx.core.operators.average import average_ - return average(key_mapper) + return average_(key_mapper) def buffer( @@ -167,9 +167,9 @@ def buffer( A function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.buffer import buffer + from rx.core.operators.buffer import buffer_ - return buffer(boundaries) + return buffer_(boundaries) def buffer_when( @@ -269,9 +269,9 @@ def buffer_with_count( A function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.buffer import buffer_with_count + from rx.core.operators.buffer import buffer_with_count_ - return buffer_with_count(count, skip) + return buffer_with_count_(count, skip) def buffer_with_time( @@ -411,9 +411,9 @@ def combine_latest( returns an observable sequence containing the result of combining elements of the sources into a tuple. """ - from rx.core.operators.combinelatest import _combine_latest + from rx.core.operators.combinelatest import combine_latest_ - return _combine_latest(*others) + return combine_latest_(*others) def concat(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -435,9 +435,9 @@ def concat(*sources: Observable[_T]) -> Callable[[Observable[_T]], Observable[_T returns an observable sequence that contains the elements of each given sequence, in sequential order. """ - from rx.core.operators.concat import _concat + from rx.core.operators.concat import concat_ - return _concat(*sources) + return concat_(*sources) def contains( @@ -467,9 +467,9 @@ def contains( whether the source sequence contains an element that has the specified value. """ - from rx.core.operators.contains import _contains + from rx.core.operators.contains import contains_ - return _contains(value, comparer) + return contains_(value, comparer) def count( @@ -501,9 +501,9 @@ def count( provided, else the count of items in the sequence. """ - from rx.core.operators.count import _count + from rx.core.operators.count import count_ - return _count(predicate) + return count_(predicate) def debounce( @@ -539,7 +539,9 @@ def debounce( throttle_with_timeout = debounce -def default_if_empty(default_value: Any = None) -> Callable[[Observable], Observable]: +def default_if_empty( + default_value: _T = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty. @@ -564,9 +566,9 @@ def default_if_empty(default_value: Any = None) -> Callable[[Observable], Observ default value if the source is empty otherwise, the elements of the source. """ - from rx.core.operators.defaultifempty import _default_if_empty + from rx.core.operators.defaultifempty import default_if_empty_ - return _default_if_empty(default_value) + return default_if_empty_(default_value) def delay_subscription( @@ -678,9 +680,9 @@ def delay( A partially applied operator function that takes the source observable and returns a time-shifted sequence. """ - from rx.core.operators.delay import _delay + from rx.core.operators.delay import delay_ - return _delay(duetime, scheduler) + return delay_(duetime, scheduler) def distinct( @@ -2135,7 +2137,7 @@ def publish_value( def reduce( - accumulator: Accumulator[_TState, _T], seed: Union[_T, NotSet] = NotSet + accumulator: Accumulator[_TState, _T], seed: Union[_TState, NotSet] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """The reduce operator. @@ -2173,14 +2175,14 @@ def reduce( return reduce(accumulator, seed) -def ref_count() -> Callable[[ConnectableObservable], Observable]: +def ref_count() -> Callable[[ConnectableObservable[_T]], Observable[_T]]: """Returns an observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence. """ - from rx.core.operators.connectable.refcount import _ref_count + from rx.core.operators.connectable.refcount import ref_count_ - return _ref_count() + return ref_count_() def repeat( From 51905e910c4fe78311fe68d83a45377c7efd36fb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 09:56:38 +0100 Subject: [PATCH 014/103] Renamings --- rx/core/operators/first.py | 4 +- rx/core/operators/firstordefault.py | 21 +++++--- rx/core/operators/flatmap.py | 8 +-- rx/core/operators/max.py | 4 +- rx/core/operators/maxby.py | 4 +- rx/core/operators/merge.py | 6 +-- rx/core/operators/min.py | 4 +- rx/core/operators/minby.py | 4 +- rx/core/operators/observeon.py | 4 +- rx/core/operators/onerrorresumenext.py | 4 +- rx/core/operators/pairwise.py | 35 +++++++----- rx/core/operators/partition.py | 39 ++++++++------ rx/operators/__init__.py | 74 ++++++++++++++------------ 13 files changed, 120 insertions(+), 91 deletions(-) diff --git a/rx/core/operators/first.py b/rx/core/operators/first.py index 37558a1e1..e539b15cf 100644 --- a/rx/core/operators/first.py +++ b/rx/core/operators/first.py @@ -4,7 +4,7 @@ from rx.core import Observable, pipe from rx.core.typing import Predicate -from .firstordefault import _first_or_default_async +from .firstordefault import first_or_default_async_ def _first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: @@ -30,4 +30,4 @@ def _first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obse if predicate: return pipe(ops.filter(predicate), ops.first()) - return _first_or_default_async(False) + return first_or_default_async_(False) diff --git a/rx/core/operators/firstordefault.py b/rx/core/operators/firstordefault.py index e693de682..714f1381c 100644 --- a/rx/core/operators/firstordefault.py +++ b/rx/core/operators/firstordefault.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, cast from rx import operators as ops from rx.core import Observable, abc, pipe @@ -8,11 +8,14 @@ _T = TypeVar("_T") -def _first_or_default_async( +def first_or_default_async_( has_default: bool = False, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def first_or_default_async(source: Observable[_T]) -> Observable[_T]: - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): def on_next(x: _T): observer.on_next(x) observer.on_completed() @@ -21,17 +24,19 @@ def on_completed(): if not has_default: observer.on_error(SequenceContainsNoElementsError()) else: - observer.on_next(default_value) + observer.on_next(cast(_T, default_value)) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) return Observable(subscribe) return first_or_default_async -def _first_or_default( +def first_or_default_( predicate: Optional[Predicate[_T]] = None, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the first element of an observable sequence that @@ -60,7 +65,7 @@ def _first_or_default( if predicate: return pipe(ops.filter(predicate), ops.first_or_default(None, default_value)) - return _first_or_default_async(True, default_value) + return first_or_default_async_(True, default_value) -__all__ = ["_first_or_default", "_first_or_default_async"] +__all__ = ["first_or_default_", "first_or_default_async_"] diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index dbb9353e0..32f62da65 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -30,7 +30,7 @@ def projection(x: _T1, i: int): return source.pipe(ops.map_indexed(projection), ops.merge_all()) -def _flat_map( +def flat_map_( mapper: Optional[Mapper[_T1, Observable[_T2]]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map(source: Observable[_T1]) -> Observable[_T2]: @@ -62,7 +62,7 @@ def flat_map(source: Observable[_T1]) -> Observable[_T2]: return flat_map -def _flat_map_indexed( +def flat_map_indexed_( mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, ) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: @@ -92,7 +92,7 @@ def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: return flat_map_indexed -def _flat_map_latest( +def flat_map_latest_( mapper: Mapper[_T1, Observable[_T2]] ) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map_latest(source: Observable[_T1]) -> Observable[_T2]: @@ -118,4 +118,4 @@ def flat_map_latest(source: Observable[_T1]) -> Observable[_T2]: return flat_map_latest -__all__ = ["_flat_map", "_flat_map_latest", "_flat_map_indexed"] +__all__ = ["flat_map_", "flat_map_latest_", "flat_map_indexed_"] diff --git a/rx/core/operators/max.py b/rx/core/operators/max.py index 0db930437..7d5cbe1cb 100644 --- a/rx/core/operators/max.py +++ b/rx/core/operators/max.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def max( +def max_( comparer: Optional[Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the maximum value in an observable sequence according to @@ -32,4 +32,4 @@ def max( return pipe(ops.max_by(identity, comparer), ops.map(first_only)) -__all__ = ["max"] +__all__ = ["max_"] diff --git a/rx/core/operators/maxby.py b/rx/core/operators/maxby.py index 147915bc6..1c89ffe61 100644 --- a/rx/core/operators/maxby.py +++ b/rx/core/operators/maxby.py @@ -9,7 +9,7 @@ _TKey = TypeVar("_TKey") -def max_by( +def max_by_( key_mapper: typing.Mapper[_T, _TKey], comparer: Optional[typing.SubComparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -37,4 +37,4 @@ def max_by(source: Observable[_T]) -> Observable[List[_T]]: return max_by -__all__ = ["max_by"] +__all__ = ["max_by_"] diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index 1ad86aeee..a99fdd3fd 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def merge( +def merge_( *sources: Observable[_T], max_concurrent: Optional[int] = None ) -> Callable[[Observable[Observable[_T]]], Observable[_T]]: def merge(source: Observable[Observable[_T]]) -> Observable[_T]: @@ -86,7 +86,7 @@ def on_completed(): return merge -def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: +def merge_all_() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: def merge_all(source: Observable[Observable[_T]]) -> Observable[_T]: """Partially applied merge_all operator. @@ -148,4 +148,4 @@ def on_completed(): return merge_all -__all__ = ["merge", "merge_all"] +__all__ = ["merge_", "merge_all_"] diff --git a/rx/core/operators/min.py b/rx/core/operators/min.py index c31ed3d60..7f94ff1ca 100644 --- a/rx/core/operators/min.py +++ b/rx/core/operators/min.py @@ -16,7 +16,7 @@ def first_only(x: Sequence[_T]) -> _T: return x[0] -def min( +def min_( comparer: Optional[Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """The `min` operator. @@ -41,4 +41,4 @@ def min( ) -__all__ = ["min"] +__all__ = ["min_"] diff --git a/rx/core/operators/minby.py b/rx/core/operators/minby.py index efd068b3b..259c55f37 100644 --- a/rx/core/operators/minby.py +++ b/rx/core/operators/minby.py @@ -56,7 +56,7 @@ def on_completed(): return Observable(subscribe) -def min_by( +def min_by_( key_mapper: typing.Mapper[_T, _TKey], comparer: Optional[typing.SubComparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -86,4 +86,4 @@ def min_by(source: Observable[_T]) -> Observable[List[_T]]: return min_by -__all__ = ["min_by"] +__all__ = ["min_by_"] diff --git a/rx/core/operators/observeon.py b/rx/core/operators/observeon.py index c65856beb..0574571f8 100644 --- a/rx/core/operators/observeon.py +++ b/rx/core/operators/observeon.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def observe_on( +def observe_on_( scheduler: abc.SchedulerBase, ) -> Callable[[Observable[_T]], Observable[_T]]: def observe_on(source: Observable[_T]) -> Observable[_T]: @@ -41,4 +41,4 @@ def subscribe( return observe_on -__all__ = ["observe_on"] +__all__ = ["observe_on_"] diff --git a/rx/core/operators/onerrorresumenext.py b/rx/core/operators/onerrorresumenext.py index 9b39a4274..50506a381 100644 --- a/rx/core/operators/onerrorresumenext.py +++ b/rx/core/operators/onerrorresumenext.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def on_error_resume_next( +def on_error_resume_next_( second: Observable[_T], ) -> Callable[[Observable[_T]], Observable[_T]]: def on_error_resume_next(source: Observable[_T]) -> Observable[_T]: @@ -17,4 +17,4 @@ def on_error_resume_next(source: Observable[_T]) -> Observable[_T]: return on_error_resume_next -__all__ = ["on_error_resume_next"] +__all__ = ["on_error_resume_next_"] diff --git a/rx/core/operators/pairwise.py b/rx/core/operators/pairwise.py index ca924697e..3a2be9365 100644 --- a/rx/core/operators/pairwise.py +++ b/rx/core/operators/pairwise.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, Optional, Tuple, TypeVar, cast -from rx.core import Observable +from rx.core import Observable, abc +_T = TypeVar("_T") -def _pairwise() -> Callable[[Observable], Observable]: - def pairwise(source: Observable) -> Observable: + +def pairwise_() -> Callable[[Observable[_T]], Observable[Tuple[_T, _T]]]: + def pairwise(source: Observable[_T]) -> Observable[Tuple[_T, _T]]: """Partially applied pairwise operator. Returns a new observable that triggers on the second and @@ -19,24 +21,33 @@ def pairwise(source: Observable) -> Observable: observations from the input observable as an array. """ - def subscribe(observer, scheduler=None): - has_previous = [False] - previous = [None] + def subscribe( + observer: abc.ObserverBase[Tuple[_T, _T]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + has_previous = False + previous: _T = cast(_T, None) - def on_next(x): + def on_next(x: _T) -> None: + nonlocal has_previous, previous pair = None with source.lock: - if has_previous[0]: - pair = (previous[0], x) + if has_previous: + pair = (previous, x) else: - has_previous[0] = True + has_previous = True - previous[0] = x + previous = x if pair: observer.on_next(pair) return source.subscribe_(on_next, observer.on_error, observer.on_completed) + return Observable(subscribe) + return pairwise + + +__all__ = ["pairwise_"] diff --git a/rx/core/operators/partition.py b/rx/core/operators/partition.py index dc527013c..81ea2085f 100644 --- a/rx/core/operators/partition.py +++ b/rx/core/operators/partition.py @@ -1,12 +1,16 @@ -from typing import Callable, List +from typing import Callable, List, TypeVar from rx import operators as ops from rx.core import Observable from rx.core.typing import Predicate, PredicateIndexed +_T = TypeVar("_T") -def _partition(predicate: Predicate) -> Callable[[Observable], List[Observable]]: - def partition(source: Observable) -> List[Observable]: + +def partition_( + predicate: Predicate[_T], +) -> Callable[[Observable[_T]], List[Observable[_T]]]: + def partition(source: Observable[_T]) -> List[Observable[_T]]: """The partially applied `partition` operator. Returns two observables which partition the observations of the @@ -27,19 +31,19 @@ def partition(source: Observable) -> List[Observable]: predicate returns False. """ - published = source.pipe( - ops.publish(), - ops.ref_count() - ) + published = source.pipe(ops.publish(), ops.ref_count()) return [ published.pipe(ops.filter(predicate)), - published.pipe(ops.filter(lambda x: not predicate(x))) + published.pipe(ops.filter(lambda x: not predicate(x))), ] + return partition -def _partition_indexed(predicate_indexed: PredicateIndexed) -> Callable[[Observable], List[Observable]]: - def partition_indexed(source: Observable) -> List[Observable]: +def partition_indexed_( + predicate_indexed: PredicateIndexed[_T], +) -> Callable[[Observable[_T]], List[Observable[_T]]]: + def partition_indexed(source: Observable[_T]) -> List[Observable[_T]]: """The partially applied indexed partition operator. Returns two observables which partition the observations of the @@ -60,12 +64,17 @@ def partition_indexed(source: Observable) -> List[Observable]: predicate returns False. """ - published = source.pipe( - ops.publish(), - ops.ref_count() - ) + published = source.pipe(ops.publish(), ops.ref_count()) return [ published.pipe(ops.filter_indexed(predicate_indexed)), - published.pipe(ops.filter_indexed(predicate_indexed=lambda x, i: not predicate_indexed(x, i))) + published.pipe( + ops.filter_indexed( + predicate_indexed=lambda x, i: not predicate_indexed(x, i) + ) + ), ] + return partition_indexed + + +__all__ = ["partition_", "partition_indexed_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index dd20911b0..90a53512c 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1163,9 +1163,9 @@ def first_or_default( observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. """ - from rx.core.operators.firstordefault import _first_or_default + from rx.core.operators.firstordefault import first_or_default_ - return _first_or_default(predicate, default_value) + return first_or_default_(predicate, default_value) def flat_map( @@ -1208,9 +1208,9 @@ def flat_map( invoking the one-to-many transform function on each element of the input sequence. """ - from rx.core.operators.flatmap import _flat_map + from rx.core.operators.flatmap import flat_map_ - return _flat_map(mapper) + return flat_map_(mapper) def flat_map_indexed( @@ -1252,9 +1252,9 @@ def flat_map_indexed( invoking the one-to-many transform function on each element of the input sequence. """ - from rx.core.operators.flatmap import _flat_map_indexed + from rx.core.operators.flatmap import flat_map_indexed_ - return _flat_map_indexed(mapper_indexed) + return flat_map_indexed_(mapper_indexed) def flat_map_latest(mapper: Mapper) -> Callable[[Observable], Observable]: @@ -1277,9 +1277,9 @@ def flat_map_latest(mapper: Mapper) -> Callable[[Observable], Observable]: point in time produces the elements of the most recent inner observable sequence that has been received. """ - from rx.core.operators.flatmap import _flat_map_latest + from rx.core.operators.flatmap import flat_map_latest_ - return _flat_map_latest(mapper) + return flat_map_latest_(mapper) def fork_join(*others: Observable) -> Callable[[Observable], Observable]: @@ -1691,9 +1691,9 @@ def max( source and returns an observable sequence containing a single element with the maximum element in the source sequence. """ - from rx.core.operators.max import max + from rx.core.operators.max import max_ - return max(comparer) + return max_(comparer) def max_by( @@ -1724,9 +1724,9 @@ def max_by( source and return an observable sequence containing a list of zero or more elements that have a maximum key value. """ - from rx.core.operators.maxby import max_by + from rx.core.operators.maxby import max_by_ - return max_by(key_mapper, comparer) + return max_by_(key_mapper, comparer) def merge( @@ -1759,9 +1759,9 @@ def merge( returns the observable sequence that merges the elements of the inner sequences. """ - from rx.core.operators.merge import merge + from rx.core.operators.merge import merge_ - return merge(*sources, max_concurrent=max_concurrent) + return merge_(*sources, max_concurrent=max_concurrent) def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: @@ -1783,9 +1783,9 @@ def merge_all() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: source and returns the observable sequence that merges the elements of the inner sequences. """ - from rx.core.operators.merge import merge_all + from rx.core.operators.merge import merge_all_ - return merge_all() + return merge_all_() def min( @@ -1815,9 +1815,9 @@ def min( returns an observable sequence containing a single element with the minimum element in the source sequence. """ - from rx.core.operators.min import min + from rx.core.operators.min import min_ - return min(comparer) + return min_(comparer) def min_by( @@ -1848,9 +1848,9 @@ def min_by( reuturns an observable sequence containing a list of zero or more elements that have a minimum key value. """ - from rx.core.operators.minby import min_by + from rx.core.operators.minby import min_by_ - return min_by(key_mapper, comparer) + return min_by_(key_mapper, comparer) def multicast( @@ -1892,7 +1892,9 @@ def multicast( return _multicast(subject, subject_factory, mapper) -def observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observable]: +def observe_on( + scheduler: abc.SchedulerBase, +) -> Callable[[Observable[_T]], Observable[_T]]: """Wraps the source sequence in order to run its observer callbacks on the specified scheduler. @@ -1908,9 +1910,9 @@ def observe_on(scheduler: abc.SchedulerBase) -> Callable[[Observable], Observabl returns the source sequence whose observations happen on the specified scheduler. """ - from rx.core.operators.observeon import observe_on + from rx.core.operators.observeon import observe_on_ - return observe_on(scheduler) + return observe_on_(scheduler) def on_error_resume_next( @@ -1938,12 +1940,12 @@ def on_error_resume_next( exceptionally. """ - from rx.core.operators.onerrorresumenext import on_error_resume_next + from rx.core.operators.onerrorresumenext import on_error_resume_next_ - return on_error_resume_next(second) + return on_error_resume_next_(second) -def pairwise() -> Callable[[Observable], Observable]: +def pairwise() -> Callable[[Observable[_T]], Observable[Tuple[_T, _T]]]: """The pairwise operator. Returns a new observable that triggers on the second and subsequent @@ -1957,12 +1959,14 @@ def pairwise() -> Callable[[Observable], Observable]: returns an observable that triggers on successive pairs of observations from the input observable as an array. """ - from rx.core.operators.pairwise import _pairwise + from rx.core.operators.pairwise import pairwise_ - return _pairwise() + return pairwise_() -def partition(predicate: Predicate) -> Callable[[Observable], List[Observable]]: +def partition( + predicate: Predicate[_T], +) -> Callable[[Observable[_T]], List[Observable[_T]]]: """Returns two observables which partition the observations of the source by the given function. The first will trigger observations for those values for which the predicate returns true. The second @@ -1989,14 +1993,14 @@ def partition(predicate: Predicate) -> Callable[[Observable], List[Observable]]: predicate returns True, and the second triggers when the predicate returns False. """ - from rx.core.operators.partition import _partition + from rx.core.operators.partition import partition_ - return _partition(predicate) + return partition_(predicate) def partition_indexed( - predicate_indexed: PredicateIndexed, -) -> Callable[[Observable], List[Observable]]: + predicate_indexed: PredicateIndexed[_T], +) -> Callable[[Observable[_T]], List[Observable[_T]]]: """The indexed partition operator. Returns two observables which partition the observations of the @@ -2024,9 +2028,9 @@ def partition_indexed( returns True, and the second triggers when the predicate returns False. """ - from rx.core.operators.partition import _partition_indexed + from rx.core.operators.partition import partition_indexed_ - return _partition_indexed(predicate_indexed) + return partition_indexed_(predicate_indexed) def pluck(key: Any) -> Callable[[Observable], Observable]: From 7e7a02f6c801585cc015599fa936db1671931440 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 10:06:35 +0100 Subject: [PATCH 015/103] Fix flaky test --- tests/test_scheduler/test_trampolinescheduler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_scheduler/test_trampolinescheduler.py b/tests/test_scheduler/test_trampolinescheduler.py index 0569a7341..4d6a2284d 100644 --- a/tests/test_scheduler/test_trampolinescheduler.py +++ b/tests/test_scheduler/test_trampolinescheduler.py @@ -9,11 +9,10 @@ class TestTrampolineScheduler(unittest.TestCase): - def test_trampoline_now(self): scheduler = TrampolineScheduler() diff = scheduler.now - default_now() - assert abs(diff) < timedelta(milliseconds=1) + assert abs(diff) < timedelta(milliseconds=2) # NOTE: diff can be 1 ms in CI def test_trampoline_now_units(self): scheduler = TrampolineScheduler() @@ -69,6 +68,7 @@ def inner_action(scheduler, state=None): ran = True return scheduler.schedule(inner_action) + scheduler.schedule(action) assert ran is True @@ -78,7 +78,6 @@ def test_trampoline_schedule_nested_order(self): tests = [] def outer(scheduler, state=None): - def action1(scheduler, state=None): tests.append(1) From e1ba5a18b8604008182426d8584dafba0da0d59b Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 10:56:57 +0100 Subject: [PATCH 016/103] More rewrites --- rx/core/notification.py | 30 +++++--- rx/core/operators/switchlatest.py | 26 ++++--- rx/core/operators/take.py | 4 +- rx/core/operators/takelast.py | 28 +++++--- rx/core/operators/takelastwithtime.py | 26 ++++--- rx/core/operators/takeuntil.py | 11 ++- rx/core/operators/takeuntilwithtime.py | 27 +++++--- rx/core/operators/takewhile.py | 45 +++++++++--- rx/core/operators/windowwithcount.py | 4 +- rx/core/operators/windowwithtime.py | 42 ++++++++---- rx/core/operators/windowwithtimeorcount.py | 44 ++++++++---- rx/core/operators/withlatestfrom.py | 4 +- rx/core/operators/zip.py | 6 +- rx/disposable/compositedisposable.py | 18 ++--- rx/disposable/multipleassignmentdisposable.py | 4 +- rx/disposable/scheduleddisposable.py | 10 +-- rx/disposable/serialdisposable.py | 11 ++- rx/internal/basic.py | 2 +- rx/internal/concurrency.py | 2 +- rx/internal/utils.py | 4 +- rx/operators/__init__.py | 68 +++++++++---------- 21 files changed, 260 insertions(+), 156 deletions(-) diff --git a/rx/core/notification.py b/rx/core/notification.py index a31034ffb..1597bc31d 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -49,7 +49,10 @@ def accept( @abstractmethod def _accept( - self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + self, + on_next: typing.OnNext[_T], + on_error: Optional[typing.OnError], + on_completed: Optional[typing.OnCompleted], ) -> None: raise NotImplementedError @@ -57,7 +60,9 @@ def _accept( def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None: raise NotImplementedError - def to_observable(self, scheduler: Optional[abc.SchedulerBase] = None) -> abc.ObservableBase[_T]: + def to_observable( + self, scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.ObservableBase[_T]: """Returns an observable sequence with a single notification, using the specified scheduler, else the immediate scheduler. @@ -72,7 +77,10 @@ def to_observable(self, scheduler: Optional[abc.SchedulerBase] = None) -> abc.Ob _scheduler = scheduler or ImmediateScheduler.singleton() - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: def action(scheduler: abc.SchedulerBase, state: Any): self._accept_observer(observer) if self.kind == "N": @@ -116,7 +124,7 @@ def _accept( def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None: return observer.on_next(self.value) - def __str__(self): + def __str__(self) -> str: val = self.value if isinstance(val, int): val = float(val) @@ -134,14 +142,17 @@ def __init__(self, exception: Exception): self.kind = "E" def _accept( - self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + self, + on_next: typing.OnNext[_T], + on_error: Optional[typing.OnError], + on_completed: Optional[typing.OnCompleted], ) -> None: return on_error(self.exception) if on_error else None def _accept_observer(self, observer: abc.ObserverBase[_T]): return observer.on_error(self.exception) - def __str__(self): + def __str__(self) -> str: return "OnError(%s)" % str(self.exception) @@ -155,14 +166,17 @@ def __init__(self): self.kind = "C" def _accept( - self, on_next: typing.OnNext[_T], on_error: Optional[typing.OnError], on_completed: Optional[typing.OnCompleted] + self, + on_next: typing.OnNext[_T], + on_error: Optional[typing.OnError], + on_completed: Optional[typing.OnCompleted], ) -> None: return on_completed() if on_completed else None def _accept_observer(self, observer: abc.ObserverBase[_T]): return observer.on_completed() - def __str__(self): + def __str__(self) -> str: return "OnCompleted()" diff --git a/rx/core/operators/switchlatest.py b/rx/core/operators/switchlatest.py index 2e9f73322..6a8f10542 100644 --- a/rx/core/operators/switchlatest.py +++ b/rx/core/operators/switchlatest.py @@ -1,18 +1,19 @@ from asyncio import Future -from typing import Any, Callable, Union, cast +from typing import Any, Callable, Optional, TypeVar, Union from rx import from_future -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import ( CompositeDisposable, SerialDisposable, SingleAssignmentDisposable, ) -from rx.internal.utils import is_future +_T = TypeVar("_T") -def switch_latest() -> Callable[[Observable], Observable]: - def switch_latest(source: Observable) -> Observable: + +def switch_latest_() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: + def switch_latest(source: Observable[Observable[_T]]) -> Observable[_T]: """Partially applied switch_latest operator. Transforms an observable sequence of observable sequences into @@ -25,13 +26,16 @@ def switch_latest(source: Observable) -> Observable: that has been received. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: inner_subscription = SerialDisposable() has_latest = [False] is_stopped = [False] latest = [0] - def on_next(inner_source: Union[Observable, Future]): + def on_next(inner_source: Union[Observable[_T], "Future[_T]"]) -> None: nonlocal source d = SingleAssignmentDisposable() @@ -42,10 +46,10 @@ def on_next(inner_source: Union[Observable, Future]): inner_subscription.disposable = d # Check if Future or Observable - if is_future(inner_source): - obs = from_future(cast(Future, inner_source)) + if isinstance(inner_source, Future): + obs = from_future(inner_source) else: - obs = cast(Observable, inner_source) + obs = inner_source def on_next(x: Any) -> None: if latest[0] == _id: @@ -80,4 +84,4 @@ def on_completed() -> None: return switch_latest -__all__ = ["switch_latest"] +__all__ = ["switch_latest_"] diff --git a/rx/core/operators/take.py b/rx/core/operators/take.py index 5c8d87998..b98851d24 100644 --- a/rx/core/operators/take.py +++ b/rx/core/operators/take.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def take_(count: int) -> Callable[[Observable[_T]], Observable[_T]]: if count < 0: raise ArgumentOutOfRangeException() @@ -51,4 +51,4 @@ def on_next(value: _T) -> None: return take -__all__ = ["_take"] +__all__ = ["take_"] diff --git a/rx/core/operators/takelast.py b/rx/core/operators/takelast.py index 1cb837dd0..d154adeb0 100644 --- a/rx/core/operators/takelast.py +++ b/rx/core/operators/takelast.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, List, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc +_T = TypeVar("_T") -def _take_last(count: int) -> Callable[[Observable], Observable]: - def take_last(source: Observable) -> Observable: + +def take_last_(count: int) -> Callable[[Observable[_T]], Observable[_T]]: + def take_last(source: Observable[_T]) -> Observable[_T]: """Returns a specified number of contiguous elements from the end of an observable sequence. @@ -25,10 +27,13 @@ def take_last(source: Observable) -> Observable: from the end of the source sequence. """ - def subscribe(observer, scheduler=None): - q = [] + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + q: List[_T] = [] - def on_next(x): + def on_next(x: _T) -> None: q.append(x) if len(q) > count: q.pop(0) @@ -38,6 +43,13 @@ def on_completed(): observer.on_next(q.pop(0)) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) + return take_last + + +__all__ = ["take_last_"] diff --git a/rx/core/operators/takelastwithtime.py b/rx/core/operators/takelastwithtime.py index 583ed3b44..e8481a483 100644 --- a/rx/core/operators/takelastwithtime.py +++ b/rx/core/operators/takelastwithtime.py @@ -1,13 +1,15 @@ -from typing import Callable, Optional +from typing import Callable, List, Optional, TypeVar, Dict, Any from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _take_last_with_time( + +def take_last_with_time_( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def take_last_with_time(source: Observable) -> Observable: +) -> Callable[[Observable[_T]], Observable[_T]]: + def take_last_with_time(source: Observable[_T]) -> Observable[_T]: """Returns elements within the specified duration from the end of the observable source sequence. @@ -29,14 +31,17 @@ def take_last_with_time(source: Observable) -> Observable: specified duration from the end of the source sequence. """ - def subscribe(observer, scheduler_=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: nonlocal duration _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() duration = _scheduler.to_timedelta(duration) - q = [] + q: List[Dict[str, Any]] = [] - def on_next(x): + def on_next(x: _T) -> None: now = _scheduler.now q.append({"interval": now, "value": x}) while q and now - q[0]["interval"] >= duration: @@ -51,8 +56,13 @@ def on_completed(): observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler_) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler_ + ) return Observable(subscribe) return take_last_with_time + + +__all__ = ["take_last_with_time_"] diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index c36656e8f..19375deb3 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -1,22 +1,21 @@ from asyncio import Future -from typing import Callable, Optional, Union, cast, TypeVar +from typing import Callable, Optional, Union, TypeVar from rx import from_future from rx.core import Observable, abc from rx.disposable import CompositeDisposable from rx.internal import noop -from rx.internal.utils import is_future _T = TypeVar("_T") -def _take_until( +def take_until_( other: Union[Observable[_T], "Future[_T]"] ) -> Callable[[Observable[_T]], Observable[_T]]: - if is_future(other): + if isinstance(other, Future): obs: Observable[_T] = from_future(other) else: - obs = cast(Observable[_T], other) + obs = other def take_until(source: Observable[_T]) -> Observable[_T]: """Returns the values from the source observable sequence until @@ -48,4 +47,4 @@ def on_completed(_: _T) -> None: return take_until -__all__ = ["_take_until"] +__all__ = ["take_until_"] diff --git a/rx/core/operators/takeuntilwithtime.py b/rx/core/operators/takeuntilwithtime.py index 67e218ab1..10aa77d4d 100644 --- a/rx/core/operators/takeuntilwithtime.py +++ b/rx/core/operators/takeuntilwithtime.py @@ -1,15 +1,18 @@ from datetime import datetime -from typing import Callable, Optional +from typing import Any, Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _take_until_with_time( - end_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def take_until_with_time(source: Observable) -> Observable: + +def take_until_with_time_( + end_time: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def take_until_with_time(source: Observable[_T]) -> Observable[_T]: """Takes elements for the specified duration until the specified end time, using the specified scheduler to run timers. @@ -24,7 +27,10 @@ def take_until_with_time(source: Observable) -> Observable: until the specified end time. """ - def subscribe(observer, scheduler_=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() if isinstance(end_time, datetime): @@ -32,12 +38,17 @@ def subscribe(observer, scheduler_=None): else: scheduler_method = _scheduler.schedule_relative - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any = None): observer.on_completed() task = scheduler_method(end_time, action) - return CompositeDisposable(task, source.subscribe(observer, scheduler=scheduler_)) + return CompositeDisposable( + task, source.subscribe(observer, scheduler=scheduler_) + ) return Observable(subscribe) return take_until_with_time + + +__all__ = ["take_until_with_time_"] diff --git a/rx/core/operators/takewhile.py b/rx/core/operators/takewhile.py index 54e611125..a75e832e7 100644 --- a/rx/core/operators/takewhile.py +++ b/rx/core/operators/takewhile.py @@ -1,11 +1,15 @@ -from typing import Any, Callable +from typing import Any, Callable, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Predicate, PredicateIndexed +_T = TypeVar("_T") -def _take_while(predicate: Predicate, inclusive: bool = False) -> Callable[[Observable], Observable]: - def take_while(source: Observable) -> Observable: + +def take_while_( + predicate: Predicate[_T], inclusive: bool = False +) -> Callable[[Observable[_T]], Observable[_T]]: + def take_while(source: Observable[_T]) -> Observable[_T]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -22,10 +26,13 @@ def take_while(source: Observable) -> Observable: test no longer passes. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: running = True - def on_next(value): + def on_next(value: _T): nonlocal running with source.lock: @@ -45,13 +52,19 @@ def on_next(value): observer.on_next(value) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return take_while -def _take_while_indexed(predicate: PredicateIndexed, inclusive: bool = False) -> Callable[[Observable], Observable]: - def take_while_indexed(source: Observable) -> Observable: +def take_while_indexed_( + predicate: PredicateIndexed[_T], inclusive: bool = False +) -> Callable[[Observable[_T]], Observable[_T]]: + def take_while_indexed(source: Observable[_T]) -> Observable[_T]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -68,7 +81,10 @@ def take_while_indexed(source: Observable) -> Observable: test no longer passes. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: running = True i = 0 @@ -94,6 +110,13 @@ def on_next(value: Any) -> None: observer.on_next(value) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return take_while_indexed + + +__all__ = ["take_while_", "take_while_indexed_"] diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index d4279013a..8b93d8475 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -12,7 +12,7 @@ _T = TypeVar("_T") -def _window_with_count( +def window_with_count_( count: int, skip: Optional[int] = None ) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more @@ -89,4 +89,4 @@ def on_completed() -> None: return window_with_count -__all__ = ["_window_with_count"] +__all__ = ["window_with_count_"] diff --git a/rx/core/operators/windowwithtime.py b/rx/core/operators/windowwithtime.py index cfd2b9b50..a94395980 100644 --- a/rx/core/operators/windowwithtime.py +++ b/rx/core/operators/windowwithtime.py @@ -1,20 +1,26 @@ from datetime import timedelta -from typing import Callable, Optional +from typing import Any, Callable, List, Optional, TypeVar from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, RefCountDisposable, - SerialDisposable, SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + RefCountDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.internal.constants import DELTA_ZERO from rx.internal.utils import add_ref from rx.scheduler import TimeoutScheduler from rx.subject import Subject +_T = TypeVar("_T") -def _window_with_time( + +def window_with_time_( timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: if timeshift is None: timeshift = timespan @@ -23,15 +29,18 @@ def _window_with_time( if not isinstance(timeshift, timedelta): timeshift = timedelta(seconds=timeshift) - def window_with_time(source: Observable) -> Observable: - def subscribe(observer, scheduler_=None): + def window_with_time(source: Observable[_T]) -> Observable[Observable[_T]]: + def subscribe( + observer: abc.ObserverBase[Observable[_T]], + scheduler_: Optional[abc.SchedulerBase] = None, + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() timer_d = SerialDisposable() next_shift = [timeshift] next_span = [timespan] total_time = [DELTA_ZERO] - q = [] + q: List[Subject[_T]] = [] group_disposable = CompositeDisposable(timer_d) ref_count_disposable = RefCountDisposable(group_disposable) @@ -60,8 +69,8 @@ def create_timer(): if is_shift: next_shift[0] += timeshift - def action(scheduler, state=None): - s = None + def action(scheduler: abc.SchedulerBase, state: Any = None): + s: Optional[Subject[_T]] = None if is_shift: s = Subject() @@ -80,25 +89,30 @@ def action(scheduler, state=None): observer.on_next(add_ref(q[0], ref_count_disposable)) create_timer() - def on_next(x): + def on_next(x: _T) -> None: for s in q: s.on_next(x) - def on_error(e): + def on_error(e: Exception) -> None: for s in q: s.on_error(e) observer.on_error(e) - def on_completed(): + def on_completed() -> None: for s in q: s.on_completed() observer.on_completed() - group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler_)) + group_disposable.add( + source.subscribe_(on_next, on_error, on_completed, scheduler_) + ) return ref_count_disposable return Observable(subscribe) return window_with_time + + +__all__ = ["window_with_time_"] diff --git a/rx/core/operators/windowwithtimeorcount.py b/rx/core/operators/windowwithtimeorcount.py index 39c7c28bc..62f14e9b9 100644 --- a/rx/core/operators/windowwithtimeorcount.py +++ b/rx/core/operators/windowwithtimeorcount.py @@ -1,22 +1,33 @@ -from typing import Callable, Optional +from typing import Any, Callable, List, Optional, TypeVar from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, RefCountDisposable, - SerialDisposable, SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + RefCountDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.internal.utils import add_ref from rx.scheduler import TimeoutScheduler from rx.subject import Subject +_T = TypeVar("_T") -def _window_with_time_or_count( - timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def window_with_time_or_count(source: Observable) -> Observable: - def subscribe(observer, scheduler_=None): + +def window_with_time_or_count_( + timespan: typing.RelativeTime, + count: int, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: + def window_with_time_or_count(source: Observable[_T]) -> Observable[Observable[_T]]: + def subscribe( + observer: abc.ObserverBase[Observable[_T]], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() n = [0] - s = [None] + s: List[Optional[Subject[_T]]] = [None] timer_d = SerialDisposable() window_id = [0] group_disposable = CompositeDisposable(timer_d) @@ -26,7 +37,7 @@ def create_timer(_id): m = SingleAssignmentDisposable() timer_d.disposable = m - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any = None): if _id != window_id[0]: return @@ -44,7 +55,7 @@ def action(scheduler, state): observer.on_next(add_ref(s[0], ref_count_disposable)) create_timer(0) - def on_next(x): + def on_next(x: _T) -> None: new_window = False new_id = 0 @@ -62,17 +73,22 @@ def on_next(x): if new_window: create_timer(new_id) - def on_error(e): + def on_error(e: Exception) -> None: s[0].on_error(e) observer.on_error(e) - def on_completed(): + def on_completed() -> None: s[0].on_completed() observer.on_completed() - group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler_)) + group_disposable.add( + source.subscribe_(on_next, on_error, on_completed, scheduler_) + ) return ref_count_disposable return Observable(subscribe) return window_with_time_or_count + + +__all__ = ["window_with_time_or_count_"] diff --git a/rx/core/operators/withlatestfrom.py b/rx/core/operators/withlatestfrom.py index e3e032dfa..9c5c312df 100644 --- a/rx/core/operators/withlatestfrom.py +++ b/rx/core/operators/withlatestfrom.py @@ -4,7 +4,7 @@ from rx.core import Observable -def with_latest_from( +def with_latest_from_( *sources: Observable[Any], ) -> Callable[[Observable[Any]], Observable[Any]]: """With latest from operator. @@ -29,4 +29,4 @@ def with_latest_from(source: Observable[Any]) -> Observable[Any]: return with_latest_from -__all__ = ["with_latest_from"] +__all__ = ["with_latest_from_"] diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index 22487357b..20b29677e 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -8,7 +8,7 @@ # pylint: disable=redefined-builtin -def zip( +def zip_( *args: Observable[Any], ) -> Callable[[Observable[Any]], Observable[Tuple[Any, ...]]]: def zip(source: Observable[Any]) -> Observable[Tuple[Any, ...]]: @@ -32,7 +32,7 @@ def zip(source: Observable[Any]) -> Observable[Tuple[Any, ...]]: return zip -def zip_with_iterable( +def zip_with_iterable_( seq: Iterable[_TOther], ) -> Callable[[Observable[_T]], Observable[Tuple[_T, _TOther]]]: def zip_with_iterable(source: Observable[_T]) -> Observable[Tuple[_T, _TOther]]: @@ -81,4 +81,4 @@ def on_next(left: _T) -> None: return zip_with_iterable -__all__ = ["zip", "zip_with_iterable"] +__all__ = ["zip_", "zip_with_iterable_"] diff --git a/rx/disposable/compositedisposable.py b/rx/disposable/compositedisposable.py index 22d21665e..b0b1cb8ac 100644 --- a/rx/disposable/compositedisposable.py +++ b/rx/disposable/compositedisposable.py @@ -1,16 +1,16 @@ from threading import RLock from typing import Any, List -from rx.core.abc import DisposableBase +from rx.core import abc -class CompositeDisposable(DisposableBase): +class CompositeDisposable(abc.DisposableBase): """Represents a group of disposable resources that are disposed together""" def __init__(self, *args: Any): if args and isinstance(args[0], list): - self.disposable: List[DisposableBase] = args[0] + self.disposable: List[abc.DisposableBase] = args[0] else: self.disposable = list(args) @@ -18,7 +18,7 @@ def __init__(self, *args: Any): self.lock = RLock() super(CompositeDisposable, self).__init__() - def add(self, item: DisposableBase): + def add(self, item: abc.DisposableBase): """Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed @@ -35,7 +35,7 @@ def add(self, item: DisposableBase): if should_dispose: item.dispose() - def remove(self, item: DisposableBase): + def remove(self, item: abc.DisposableBase): """Removes and disposes the first occurrence of a disposable from the CompositeDisposable.""" @@ -80,7 +80,7 @@ def clear(self) -> None: for disposable in current_disposable: disposable.dispose() - def contains(self, item: DisposableBase) -> bool: + def contains(self, item: abc.DisposableBase) -> bool: """Determines whether the CompositeDisposable contains a specific disposable. @@ -92,12 +92,12 @@ def contains(self, item: DisposableBase) -> bool: return item in self.disposable - def to_list(self): + def to_list(self) -> List[abc.DisposableBase]: return self.disposable[:] - def __len__(self): + def __len__(self) -> int: return len(self.disposable) @property - def length(self): + def length(self) -> int: return len(self.disposable) diff --git a/rx/disposable/multipleassignmentdisposable.py b/rx/disposable/multipleassignmentdisposable.py index df6c0c83c..e8a0bb5ab 100644 --- a/rx/disposable/multipleassignmentdisposable.py +++ b/rx/disposable/multipleassignmentdisposable.py @@ -18,7 +18,7 @@ def __init__(self): def get_disposable(self) -> Optional[DisposableBase]: return self.current - def set_disposable(self, value: DisposableBase): + def set_disposable(self, value: DisposableBase) -> None: """If the MultipleAssignmentDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object.""" @@ -33,7 +33,7 @@ def set_disposable(self, value: DisposableBase): disposable = property(get_disposable, set_disposable) - def dispose(self): + def dispose(self) -> None: """Disposes the underlying disposable as well as all future replacements.""" diff --git a/rx/disposable/scheduleddisposable.py b/rx/disposable/scheduleddisposable.py index 04acb36e6..81e325d22 100644 --- a/rx/disposable/scheduleddisposable.py +++ b/rx/disposable/scheduleddisposable.py @@ -1,14 +1,16 @@ from threading import RLock from typing import Any -from rx.core.abc import DisposableBase, SchedulerBase +from rx.core import abc -class ScheduledDisposable(DisposableBase): +class ScheduledDisposable(abc.DisposableBase): """Represents a disposable resource whose disposal invocation will be scheduled on the specified Scheduler""" - def __init__(self, scheduler: SchedulerBase, disposable: DisposableBase) -> None: + def __init__( + self, scheduler: abc.SchedulerBase, disposable: abc.DisposableBase + ) -> None: """Initializes a new instance of the ScheduledDisposable class that uses a Scheduler on which to dispose the disposable.""" @@ -24,7 +26,7 @@ def dispose(self) -> None: parent = self - def action(scheduler: SchedulerBase, state: Any): + def action(scheduler: abc.SchedulerBase, state: Any): """Scheduled dispose action""" should_dispose = False diff --git a/rx/disposable/serialdisposable.py b/rx/disposable/serialdisposable.py index cfea05be0..78ca83372 100644 --- a/rx/disposable/serialdisposable.py +++ b/rx/disposable/serialdisposable.py @@ -2,23 +2,22 @@ from typing import Optional from rx.core import abc -from rx.core.abc import DisposableBase -class SerialDisposable(DisposableBase): +class SerialDisposable(abc.DisposableBase): """Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource. """ def __init__(self) -> None: - self.current: Optional[DisposableBase] = None + self.current: Optional[abc.DisposableBase] = None self.is_disposed = False self.lock = RLock() super().__init__() - def get_disposable(self) -> Optional[DisposableBase]: + def get_disposable(self) -> Optional[abc.DisposableBase]: return self.current def set_disposable(self, value: abc.DisposableBase) -> None: @@ -27,7 +26,7 @@ def set_disposable(self, value: abc.DisposableBase) -> None: disposable object. Assigning this property disposes the previous disposable object.""" - old: Optional[DisposableBase] = None + old: Optional[abc.DisposableBase] = None with self.lock: should_dispose = self.is_disposed @@ -47,7 +46,7 @@ def dispose(self) -> None: """Disposes the underlying disposable as well as all future replacements.""" - old: Optional[DisposableBase] = None + old: Optional[abc.DisposableBase] = None with self.lock: if not self.is_disposed: diff --git a/rx/internal/basic.py b/rx/internal/basic.py index 4c42afc1d..34348a205 100644 --- a/rx/internal/basic.py +++ b/rx/internal/basic.py @@ -18,7 +18,7 @@ def default_now() -> datetime: return datetime.utcnow() -def default_comparer(x: Any, y: Any) -> bool: +def default_comparer(x: _T, y: _T) -> bool: return x == y diff --git a/rx/internal/concurrency.py b/rx/internal/concurrency.py index 6af9021d6..fdcf57d8e 100644 --- a/rx/internal/concurrency.py +++ b/rx/internal/concurrency.py @@ -8,7 +8,7 @@ def default_thread_factory(target: StartableTarget) -> Thread: return Thread(target=target, daemon=True) -def synchronized(lock: RLock): +def synchronized(lock: RLock) -> Callable[[Callable[..., Any]], Callable[..., Any]]: """A decorator for synchronizing access to a given function.""" def wrapper(fn: Callable[..., Any]) -> Callable[..., Any]: diff --git a/rx/internal/utils.py b/rx/internal/utils.py index fa40aedcc..7bc6dbf19 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -52,8 +52,8 @@ def alias(name: str, doc: str, fun: Callable[..., Any]) -> Callable[..., Any]: class NotSet: """Sentinel value.""" - def __eq__(self, other: Any): + def __eq__(self, other: Any) -> bool: return self is other - def __repr__(self): + def __repr__(self) -> str: return "NotSet" diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 90a53512c..d1e5c9a50 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2983,7 +2983,7 @@ def sum( return _sum(key_mapper) -def switch_latest() -> Callable[[Observable[Observable[Any]]], Observable[Any]]: +def switch_latest() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: """The switch_latest operator. Transforms an observable sequence of observable sequences into an @@ -3005,9 +3005,9 @@ def switch_latest() -> Callable[[Observable[Observable[Any]]], Observable[Any]]: time produces the elements of the most recent inner observable sequence that has been received. """ - from rx.core.operators.switchlatest import switch_latest + from rx.core.operators.switchlatest import switch_latest_ - return switch_latest() + return switch_latest_() def take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3032,9 +3032,9 @@ def take(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence that contains the specified number of elements from the start of the input sequence. """ - from rx.core.operators.take import _take + from rx.core.operators.take import take_ - return _take(count) + return take_(count) def take_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3065,9 +3065,9 @@ def take_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence containing the specified number of elements from the end of the source sequence. """ - from rx.core.operators.takelast import _take_last + from rx.core.operators.takelast import take_last_ - return _take_last(count) + return take_last_(count) def take_last_buffer(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3138,9 +3138,9 @@ def take_last_with_time( during the specified duration from the end of the source sequence. """ - from rx.core.operators.takelastwithtime import _take_last_with_time + from rx.core.operators.takelastwithtime import take_last_with_time_ - return _take_last_with_time(duration, scheduler=scheduler) + return take_last_with_time_(duration, scheduler=scheduler) def take_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3165,9 +3165,9 @@ def take_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[ source sequence up to the point the other sequence interrupted further propagation. """ - from rx.core.operators.takeuntil import _take_until + from rx.core.operators.takeuntil import take_until_ - return _take_until(other) + return take_until_(other) def take_until_with_time( @@ -3200,14 +3200,14 @@ def take_until_with_time( returns an observable sequence with the elements taken until the specified end time. """ - from rx.core.operators.takeuntilwithtime import _take_until_with_time + from rx.core.operators.takeuntilwithtime import take_until_with_time_ - return _take_until_with_time(end_time, scheduler=scheduler) + return take_until_with_time_(end_time, scheduler=scheduler) def take_while( - predicate: Predicate, inclusive: bool = False -) -> Callable[[Observable], Observable]: + predicate: Predicate[_T], inclusive: bool = False +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -3234,9 +3234,9 @@ def take_while( the input sequence that occur before the element at which the test no longer passes. """ - from rx.core.operators.takewhile import _take_while + from rx.core.operators.takewhile import take_while_ - return _take_while(predicate, inclusive) + return take_while_(predicate, inclusive) def take_while_indexed( @@ -3269,9 +3269,9 @@ def take_while_indexed( input sequence that occur before the element at which the test no longer passes. """ - from rx.core.operators.takewhile import _take_while_indexed + from rx.core.operators.takewhile import take_while_indexed_ - return _take_while_indexed(predicate, inclusive) + return take_while_indexed_(predicate, inclusive) def take_with_time( @@ -3697,7 +3697,7 @@ def window_toggle( def window_with_count( count: int, skip: Optional[int] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows which are produced based on element count information. @@ -3723,29 +3723,29 @@ def window_with_count( Returns: An observable sequence of windows. """ - from rx.core.operators.windowwithcount import _window_with_count + from rx.core.operators.windowwithcount import window_with_count_ - return _window_with_count(count, skip) + return window_with_count_(count, skip) def window_with_time( timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: - from rx.core.operators.windowwithtime import _window_with_time +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: + from rx.core.operators.windowwithtime import window_with_time_ - return _window_with_time(timespan, timeshift, scheduler) + return window_with_time_(timespan, timeshift, scheduler) def window_with_time_or_count( timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: - from rx.core.operators.windowwithtimeorcount import _window_with_time_or_count +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: + from rx.core.operators.windowwithtimeorcount import window_with_time_or_count_ - return _window_with_time_or_count(timespan, count, scheduler) + return window_with_time_or_count_(timespan, count, scheduler) def with_latest_from( @@ -3775,9 +3775,9 @@ def with_latest_from( returns an observable sequence containing the result of combining elements of the sources into a tuple. """ - from rx.core.operators.withlatestfrom import with_latest_from + from rx.core.operators.withlatestfrom import with_latest_from_ - return with_latest_from(*sources) + return with_latest_from_(*sources) def zip(*args: Observable[Any]) -> Callable[[Observable[Any]], Observable[Any]]: @@ -3806,9 +3806,9 @@ def zip(*args: Observable[Any]) -> Callable[[Observable[Any]], Observable[Any]]: returns an observable sequence containing the result of combining elements of the sources as a tuple. """ - from rx.core.operators.zip import zip + from rx.core.operators.zip import zip_ - return zip(*args) + return zip_(*args) def zip_with_iterable( @@ -3837,9 +3837,9 @@ def zip_with_iterable( returns an observable sequence containing the result of combining elements of the sources as a tuple. """ - from rx.core.operators.zip import zip_with_iterable + from rx.core.operators.zip import zip_with_iterable_ - return zip_with_iterable(second) + return zip_with_iterable_(second) zip_with_list = zip_with_iterable From 72a169567b4211feb8a5e387e8fd9f80666a788e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 15:56:47 +0100 Subject: [PATCH 017/103] Rename functions to valid names --- rx/core/observable/observable.py | 8 +- rx/core/operators/do.py | 42 ++++-- rx/core/operators/filter.py | 23 ++- rx/core/operators/first.py | 11 +- rx/core/operators/ignoreelements.py | 25 +++- rx/core/operators/isempty.py | 4 +- rx/core/operators/last.py | 4 +- rx/core/operators/publish.py | 9 +- rx/core/operators/reduce.py | 4 +- rx/core/operators/repeat.py | 2 +- rx/core/operators/retry.py | 4 +- rx/core/operators/sample.py | 4 +- rx/core/operators/scan.py | 4 +- rx/core/operators/sequenceequal.py | 4 +- rx/core/operators/single.py | 4 +- rx/core/operators/singleordefault.py | 8 +- rx/core/operators/skip.py | 4 +- rx/core/operators/skiplast.py | 4 +- rx/core/operators/skiplastwithtime.py | 4 +- rx/core/operators/skipuntil.py | 4 +- rx/core/operators/skipuntilwithtime.py | 4 +- rx/core/operators/skipwhile.py | 6 +- rx/core/operators/skipwithtime.py | 4 +- rx/core/operators/slice.py | 4 +- rx/core/operators/some.py | 14 +- rx/core/operators/startswith.py | 4 +- rx/core/operators/subscribeon.py | 4 +- rx/core/operators/sum.py | 4 +- rx/core/operators/takelastbuffer.py | 30 ++-- rx/core/operators/takewithtime.py | 24 ++- rx/core/operators/throttlefirst.py | 4 +- rx/core/operators/timeinterval.py | 2 +- rx/core/operators/timeout.py | 2 +- rx/core/operators/timeoutwithmapper.py | 4 +- rx/core/operators/timestamp.py | 4 +- rx/core/operators/todict.py | 4 +- rx/core/operators/tofuture.py | 4 +- rx/core/operators/toiterable.py | 4 +- rx/core/operators/toset.py | 4 +- rx/core/operators/whiledo.py | 4 +- rx/operators/__init__.py | 200 +++++++++++++------------ 41 files changed, 294 insertions(+), 216 deletions(-) diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index b9228c8c8..26276f77b 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -335,10 +335,10 @@ def __await__(self) -> Iterable[_T]: Returns: The last item of the observable sequence. """ - from ..operators.tofuture import to_future + from ..operators.tofuture import to_future_ loop = asyncio.get_event_loop() - return iter(self.pipe(to_future(scheduler=AsyncIOScheduler(loop=loop)))) + return iter(self.pipe(to_future_(scheduler=AsyncIOScheduler(loop=loop)))) def __add__(self, other: Observable[_T]) -> Observable[_T]: """Pythonic version of :func:`concat `. @@ -419,9 +419,9 @@ def __getitem__(self, key: slice) -> Observable[_T]: else: raise TypeError("Invalid argument type.") - from ..operators.slice import _slice + from ..operators.slice import slice_ - return _slice(start, stop, step)(self) + return slice_(start, stop, step)(self) __all__ = ["Observable"] diff --git a/rx/core/operators/do.py b/rx/core/operators/do.py index 5d720a1a3..f9e7a5d20 100644 --- a/rx/core/operators/do.py +++ b/rx/core/operators/do.py @@ -1,15 +1,17 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable +_T = TypeVar("_T") -def _do_action( - on_next: Optional[typing.OnNext] = None, + +def do_action_( + on_next: Optional[typing.OnNext[_T]] = None, on_error: Optional[typing.OnError] = None, on_completed: Optional[typing.OnCompleted] = None, -) -> Callable[[Observable], Observable]: - def do_action(source: Observable) -> Observable: +) -> Callable[[Observable[_T]], Observable[_T]]: + def do_action(source: Observable[_T]) -> Observable[_T]: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional termination of the observable sequence. This method can be used @@ -35,8 +37,11 @@ def do_action(source: Observable) -> Observable: behavior applied. """ - def subscribe(observer, scheduler=None): - def _on_next(x): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + def _on_next(x: _T) -> None: if not on_next: observer.on_next(x) else: @@ -47,7 +52,7 @@ def _on_next(x): observer.on_next(x) - def _on_error(exception): + def _on_error(exception: Exception) -> None: if not on_error: observer.on_error(exception) else: @@ -58,7 +63,7 @@ def _on_error(exception): observer.on_error(exception) - def _on_completed(): + def _on_completed() -> None: if not on_completed: observer.on_completed() else: @@ -76,7 +81,7 @@ def _on_completed(): return do_action -def do(observer: abc.ObserverBase) -> Callable[[Observable], Observable]: +def do_(observer: abc.ObserverBase[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Invokes an action for each element in the observable sequence and invokes an action on graceful or exceptional termination of the observable sequence. This method can be used for debugging, logging, @@ -94,7 +99,7 @@ def do(observer: abc.ObserverBase) -> Callable[[Observable], Observable]: applied. """ - return _do_action(observer.on_next, observer.on_error, observer.on_completed) + return do_action_(observer.on_next, observer.on_error, observer.on_completed) def do_after_next(source, after_next): @@ -129,7 +134,9 @@ def do_on_subscribe(source: Observable, on_subscribe): def subscribe(observer, scheduler=None): on_subscribe() - return source.subscribe_(observer.on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + observer.on_next, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) @@ -151,7 +158,9 @@ def dispose(self) -> None: def subscribe(observer, scheduler=None): composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose()) - subscription = source.subscribe_(observer.on_next, observer.on_error, observer.on_completed, scheduler) + subscription = source.subscribe_( + observer.on_next, observer.on_error, observer.on_completed, scheduler + ) composite_disposable.add(subscription) return composite_disposable @@ -267,7 +276,9 @@ def on_error(exception): composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose(was_invoked)) - subscription = source.subscribe_(observer.on_next, on_error, on_completed, scheduler) + subscription = source.subscribe_( + observer.on_next, on_error, on_completed, scheduler + ) composite_disposable.add(subscription) return composite_disposable @@ -275,3 +286,6 @@ def on_error(exception): return Observable(subscribe) return partial + + +__all__ = ["do_", "do_action_"] diff --git a/rx/core/operators/filter.py b/rx/core/operators/filter.py index 19354a899..3bc0d9e2f 100644 --- a/rx/core/operators/filter.py +++ b/rx/core/operators/filter.py @@ -5,8 +5,9 @@ _T = TypeVar("_T") + # pylint: disable=redefined-builtin -def _filter(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def filter_(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: def filter(source: Observable[_T]) -> Observable[_T]: """Partially applied filter operator. @@ -23,7 +24,9 @@ def filter(source: Observable[_T]) -> Observable[_T]: A filtered observable sequence. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase]) -> abc.DisposableBase: + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] + ) -> abc.DisposableBase: def on_next(value: _T): try: should_run = predicate(value) @@ -34,14 +37,16 @@ def on_next(value: _T): if should_run: observer.on_next(value) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) return filter -def _filter_indexed( +def filter_indexed_( predicate_indexed: Optional[PredicateIndexed[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: def filter_indexed(source: Observable[_T]) -> Observable[_T]: @@ -60,7 +65,9 @@ def filter_indexed(source: Observable[_T]) -> Observable[_T]: A filtered observable sequence. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase]): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] + ): count = 0 def on_next(value: _T): @@ -76,11 +83,13 @@ def on_next(value: _T): if should_run: observer.on_next(value) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) return filter_indexed -__all__ = ["_filter", "_filter_indexed"] +__all__ = ["filter_", "filter_indexed_"] diff --git a/rx/core/operators/first.py b/rx/core/operators/first.py index e539b15cf..6a6b7d242 100644 --- a/rx/core/operators/first.py +++ b/rx/core/operators/first.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe @@ -6,8 +6,12 @@ from .firstordefault import first_or_default_async_ +_T = TypeVar("_T") -def _first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: + +def first_( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the first element of an observable sequence that satisfies the condition in the predicate if present else the first item in the sequence. @@ -31,3 +35,6 @@ def _first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obse return pipe(ops.filter(predicate), ops.first()) return first_or_default_async_(False) + + +__all__ = ["first_"] diff --git a/rx/core/operators/ignoreelements.py b/rx/core/operators/ignoreelements.py index 6c222cc90..5b10c8667 100644 --- a/rx/core/operators/ignoreelements.py +++ b/rx/core/operators/ignoreelements.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, abc from rx.internal import noop +_T = TypeVar("_T") -def _ignore_elements() -> Callable[[Observable], Observable]: + +def ignore_elements_() -> Callable[[Observable[_T]], Observable[_T]]: """Ignores all elements in an observable sequence leaving only the termination messages. @@ -13,9 +15,18 @@ def _ignore_elements() -> Callable[[Observable], Observable]: termination, successful or exceptional, of the source sequence. """ - def ignore_elements(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - return source.subscribe_(noop, observer.on_error, observer.on_completed, scheduler) + def ignore_elements(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + return source.subscribe_( + noop, observer.on_error, observer.on_completed, scheduler + ) return Observable(subscribe) - return ignore_elements \ No newline at end of file + + return ignore_elements + + +__all__ = ["ignore_elements_"] diff --git a/rx/core/operators/isempty.py b/rx/core/operators/isempty.py index 7c9324276..07e65a196 100644 --- a/rx/core/operators/isempty.py +++ b/rx/core/operators/isempty.py @@ -4,7 +4,7 @@ from rx.core import Observable, pipe -def _is_empty() -> Callable[[Observable[Any]], Observable[bool]]: +def is_empty_() -> Callable[[Observable[Any]], Observable[bool]]: """Determines whether an observable sequence is empty. Returns: @@ -15,4 +15,4 @@ def _is_empty() -> Callable[[Observable[Any]], Observable[bool]]: return pipe(ops.some(), ops.map(lambda b: not b)) -__all__ = ["_is_empty"] +__all__ = ["is_empty_"] diff --git a/rx/core/operators/last.py b/rx/core/operators/last.py index 4d07aa908..93b5989ec 100644 --- a/rx/core/operators/last.py +++ b/rx/core/operators/last.py @@ -9,7 +9,7 @@ _T = TypeVar("_T") -def _last(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def last_(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: def last(source: Observable[_T]) -> Observable[_T]: """Partially applied last operator. @@ -37,4 +37,4 @@ def last(source: Observable[_T]) -> Observable[_T]: return last -__all__ = ["_last"] +__all__ = ["last_"] diff --git a/rx/core/operators/publish.py b/rx/core/operators/publish.py index 79a327d99..0dcd58bd7 100644 --- a/rx/core/operators/publish.py +++ b/rx/core/operators/publish.py @@ -9,7 +9,7 @@ _TResult = TypeVar("_TResult") -def _publish( +def publish_( mapper: Optional[Mapper[_T, _TResult]] = None, ) -> Callable[[Observable[_T]], ConnectableObservable[_TResult]]: """Returns an observable sequence that is the result of invoking the @@ -39,7 +39,7 @@ def _publish( return pipe(ops.multicast(subject=Subject())) -def share() -> Callable[[Observable[_T]], Observable[_T]]: +def share_() -> Callable[[Observable[_T]], Observable[_T]]: """Share a single subscription among multple observers. Returns a new Observable that multicasts (shares) the original @@ -50,4 +50,7 @@ def share() -> Callable[[Observable[_T]], Observable[_T]]: This is an alias for a composed publish() and ref_count(). """ - return pipe(_publish(), ops.ref_count()) + return pipe(publish_(), ops.ref_count()) + + +__all__ = ["publish_", "share_"] diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 6059a06bf..98d1fbc54 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -9,7 +9,7 @@ _TState = TypeVar("_TState") -def reduce( +def reduce_( accumulator: Accumulator[_TState, _T], seed: Any = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """Applies an accumulator function over an observable sequence, @@ -41,4 +41,4 @@ def reduce( return pipe(ops.scan(accumulator), ops.last()) -__all__ = ["reduce"] +__all__ = ["reduce_"] diff --git a/rx/core/operators/repeat.py b/rx/core/operators/repeat.py index 461ed5233..f9f3d560d 100644 --- a/rx/core/operators/repeat.py +++ b/rx/core/operators/repeat.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def repeat( +def repeat_( repeat_count: Optional[int] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: if repeat_count is None: diff --git a/rx/core/operators/retry.py b/rx/core/operators/retry.py index ce01e0b3c..ec5b797c9 100644 --- a/rx/core/operators/retry.py +++ b/rx/core/operators/retry.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def retry( +def retry_( retry_count: Optional[int] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats the source observable sequence the specified number of @@ -38,4 +38,4 @@ def retry(source: Observable[_T]) -> Observable[_T]: return retry -__all__ = ["retry"] +__all__ = ["retry_"] diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index 2c06f1725..d1d513664 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -45,7 +45,7 @@ def on_completed(): return Observable(subscribe) -def sample( +def sample_( sampler: Union[typing.RelativeTime, Observable[Any]], scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: @@ -70,4 +70,4 @@ def sample(source: Observable[_T]) -> Observable[_T]: return sample -__all__ = ["sample"] +__all__ = ["sample_"] diff --git a/rx/core/operators/scan.py b/rx/core/operators/scan.py index feced7390..d4d19d6d3 100644 --- a/rx/core/operators/scan.py +++ b/rx/core/operators/scan.py @@ -10,7 +10,7 @@ _TState = TypeVar("_TState") -def _scan( +def scan_( accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: has_seed = seed is not NotSet @@ -54,4 +54,4 @@ def projection(x: _T) -> _TState: return scan -__all__ = ["_scan"] +__all__ = ["scan_"] diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index ec5067867..b563cf4ee 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _sequence_equal( +def _sequence_equal_( second: Observable[_T], comparer: Optional[typing.Comparer[_T]] = None ) -> Callable[[Observable[_T]], Observable[bool]]: comparer = comparer or default_comparer @@ -116,4 +116,4 @@ def on_completed2(): return sequence_equal -__all__ = ["_sequence_equal"] +__all__ = ["_sequence_equal_"] diff --git a/rx/core/operators/single.py b/rx/core/operators/single.py index e77a3f3db..1534b5900 100644 --- a/rx/core/operators/single.py +++ b/rx/core/operators/single.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def single( +def single_( predicate: Optional[Predicate[_T]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that satisfies the @@ -33,4 +33,4 @@ def single( return ops.single_or_default_async(False) -__all__ = ["single"] +__all__ = ["single_"] diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index 9f755db97..d49f4b2e0 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def single_or_default_async( +def single_or_default_async_( has_default: bool = False, default_value: _T = None ) -> Callable[[Observable[_T]], Observable[_T]]: def single_or_default_async(source: Observable[_T]) -> Observable[_T]: @@ -44,7 +44,7 @@ def on_completed(): return single_or_default_async -def single_or_default( +def single_or_default_( predicate: Optional[Predicate[_T]] = None, default_value: _T = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the only element of an observable sequence that matches @@ -73,7 +73,7 @@ def single_or_default( if predicate: return pipe(ops.filter(predicate), ops.single_or_default(None, default_value)) else: - return single_or_default_async(True, default_value) + return single_or_default_async_(True, default_value) -__all__ = ["single_or_default", "single_or_default_async"] +__all__ = ["single_or_default_", "single_or_default_async_"] diff --git a/rx/core/operators/skip.py b/rx/core/operators/skip.py index af8f1bab5..fe9532744 100644 --- a/rx/core/operators/skip.py +++ b/rx/core/operators/skip.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_(count: int) -> Callable[[Observable[_T]], Observable[_T]]: if count < 0: raise ArgumentOutOfRangeException() @@ -47,4 +47,4 @@ def on_next(value: _T) -> None: return skip -__all__ = ["skip"] +__all__ = ["skip_"] diff --git a/rx/core/operators/skiplast.py b/rx/core/operators/skiplast.py index dda41ac47..4bf1ff3bc 100644 --- a/rx/core/operators/skiplast.py +++ b/rx/core/operators/skiplast.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_last_(count: int) -> Callable[[Observable[_T]], Observable[_T]]: def skip_last(source: Observable[_T]) -> Observable[_T]: """Bypasses a specified number of elements at the end of an observable sequence. @@ -49,4 +49,4 @@ def on_next(value: _T) -> None: return skip_last -__all__ = ["skip_last"] +__all__ = ["skip_last_"] diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index e97802ee4..9721b6be1 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def skip_last_with_time( +def skip_last_with_time_( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Skips elements for the specified duration from the end of the @@ -66,4 +66,4 @@ def on_completed() -> None: return skip_last_with_time -__all__ = ["skip_last_with_time"] +__all__ = ["skip_last_with_time_"] diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index 67db07843..0d3a6a547 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -9,7 +9,7 @@ _T = TypeVar("_T") -def skip_until( +def skip_until_( other: Union[Observable[_T], "Future[_T]"] ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after @@ -71,4 +71,4 @@ def on_completed2(): return skip_until -__all__ = ["skip_until"] +__all__ = ["skip_until_"] diff --git a/rx/core/operators/skipuntilwithtime.py b/rx/core/operators/skipuntilwithtime.py index 8aeaf85d0..1370ab996 100644 --- a/rx/core/operators/skipuntilwithtime.py +++ b/rx/core/operators/skipuntilwithtime.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _skip_until_with_time( +def skip_until_with_time_( start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def skip_until_with_time(source: Observable[_T]) -> Observable[_T]: @@ -60,4 +60,4 @@ def action(scheduler: abc.SchedulerBase, state: Any): return skip_until_with_time -__all__ = ["_skip_until_with_time"] +__all__ = ["skip_until_with_time_"] diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index 7a702f95b..0c2a064df 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def skip_while( +def skip_while_( predicate: typing.Predicate[_T], ) -> Callable[[Observable[_T]], Observable[_T]]: def skip_while(source: Observable[_T]) -> Observable[_T]: @@ -55,7 +55,7 @@ def on_next(value: _T): return skip_while -def skip_while_indexed( +def skip_while_indexed_( predicate: typing.PredicateIndexed[_T], ) -> Callable[[Observable[_T]], Observable[_T]]: return pipe( @@ -65,4 +65,4 @@ def skip_while_indexed( ) -__all__ = ["skip_while", "skip_while_indexed"] +__all__ = ["skip_while_", "skip_while_indexed_"] diff --git a/rx/core/operators/skipwithtime.py b/rx/core/operators/skipwithtime.py index d227f8eda..c73616062 100644 --- a/rx/core/operators/skipwithtime.py +++ b/rx/core/operators/skipwithtime.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _skip_with_time( +def skip_with_time_( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def skip_with_time(source: Observable[_T]) -> Observable[_T]: @@ -58,4 +58,4 @@ def on_next(x: _T): return skip_with_time -__all__ = ["_skip_with_time"] +__all__ = ["skip_with_time_"] diff --git a/rx/core/operators/slice.py b/rx/core/operators/slice.py index c929ccaa9..9b629a28a 100644 --- a/rx/core/operators/slice.py +++ b/rx/core/operators/slice.py @@ -8,7 +8,7 @@ # pylint: disable=redefined-builtin -def _slice( +def slice_( start: Optional[int] = None, stop: Optional[int] = None, step: Optional[int] = None ) -> Callable[[Observable[_T]], Observable[_T]]: _start: int = 0 if start is None else start @@ -72,4 +72,4 @@ def slice(source: Observable[_T]) -> Observable[_T]: return slice -__all__ = ["_slice"] +__all__ = ["slice_"] diff --git a/rx/core/operators/some.py b/rx/core/operators/some.py index 38848a4ed..bf459995d 100644 --- a/rx/core/operators/some.py +++ b/rx/core/operators/some.py @@ -7,7 +7,9 @@ _T = TypeVar("_T") -def _some(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[bool]]: +def some_( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[bool]]: def some(source: Observable[_T]) -> Observable[bool]: """Partially applied operator. @@ -27,7 +29,10 @@ def some(source: Observable[_T]) -> Observable[bool]: some items are in the sequence. """ - def subscribe(observer: abc.ObserverBase[bool], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[bool], + scheduler: Optional[abc.SchedulerBase] = None, + ): def on_next(_: _T): observer.on_next(True) observer.on_completed() @@ -41,9 +46,12 @@ def on_error(): if predicate: return source.pipe( ops.filter(predicate), - _some(), + some_(), ) return Observable(subscribe) return some + + +__all__ = ["some_"] diff --git a/rx/core/operators/startswith.py b/rx/core/operators/startswith.py index 30a896500..44a946e5b 100644 --- a/rx/core/operators/startswith.py +++ b/rx/core/operators/startswith.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: +def start_with_(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: def start_with(source: Observable[_T]) -> Observable[_T]: """Partially applied start_with operator. @@ -25,4 +25,4 @@ def start_with(source: Observable[_T]) -> Observable[_T]: return start_with -__all__ = ["start_with"] +__all__ = ["start_with_"] diff --git a/rx/core/operators/subscribeon.py b/rx/core/operators/subscribeon.py index 578b338da..72d5a0234 100644 --- a/rx/core/operators/subscribeon.py +++ b/rx/core/operators/subscribeon.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def subscribe_on( +def subscribe_on_( scheduler: abc.SchedulerBase, ) -> Callable[[Observable[_T]], Observable[_T]]: def subscribe_on(source: Observable[_T]) -> Observable[_T]: @@ -54,4 +54,4 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Any] = None): return subscribe_on -__all__ = ["subscribe_on"] +__all__ = ["subscribe_on_"] diff --git a/rx/core/operators/sum.py b/rx/core/operators/sum.py index 058b3f0b9..b0d29dc53 100644 --- a/rx/core/operators/sum.py +++ b/rx/core/operators/sum.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def _sum( +def sum_( key_mapper: Optional[Mapper[_T, int]] = None ) -> Callable[[Observable[_T]], Observable[int]]: if key_mapper: @@ -16,4 +16,4 @@ def _sum( return ops.reduce(seed=0, accumulator=lambda prev, curr: prev + curr) -__all__ = ["_sum"] +__all__ = ["sum_"] diff --git a/rx/core/operators/takelastbuffer.py b/rx/core/operators/takelastbuffer.py index 4620d040c..4126ce981 100644 --- a/rx/core/operators/takelastbuffer.py +++ b/rx/core/operators/takelastbuffer.py @@ -1,10 +1,12 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar, List -from rx.core import Observable +from rx.core import Observable, abc +_T = TypeVar("_T") -def _take_last_buffer(count: int) -> Callable[[Observable], Observable]: - def take_last_buffer(source: Observable) -> Observable: + +def take_last_buffer_(count: int) -> Callable[[Observable[_T]], Observable[List[_T]]]: + def take_last_buffer(source: Observable[_T]) -> Observable[List[_T]]: """Returns an array with the specified number of contiguous elements from the end of an observable sequence. @@ -25,19 +27,29 @@ def take_last_buffer(source: Observable) -> Observable: sequence. """ - def subscribe(observer, scheduler=None): - q = [] + def subscribe( + observer: abc.ObserverBase[List[_T]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + q: List[_T] = [] - def on_next(x): + def on_next(x: _T) -> None: with source.lock: q.append(x) if len(q) > count: q.pop(0) - def on_completed(): + def on_completed() -> None: observer.on_next(q) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) + return take_last_buffer + + +__all__ = ["take_last_buffer_"] diff --git a/rx/core/operators/takewithtime.py b/rx/core/operators/takewithtime.py index 0cd44154d..0eabe4431 100644 --- a/rx/core/operators/takewithtime.py +++ b/rx/core/operators/takewithtime.py @@ -1,14 +1,16 @@ -from typing import Callable, Optional +from typing import Any, Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -def _take_with_time( + +def take_with_time_( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: - def take_with_time(source: Observable) -> Observable: +) -> Callable[[Observable[_T]], Observable[_T]]: + def take_with_time(source: Observable[_T]) -> Observable[_T]: """Takes elements for the specified duration from the start of the observable source sequence. @@ -29,15 +31,23 @@ def take_with_time(source: Observable) -> Observable: specified duration from the start of the source sequence. """ - def subscribe(observer, scheduler_=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any = None): observer.on_completed() disp = _scheduler.schedule_relative(duration, action) - return CompositeDisposable(disp, source.subscribe(observer, scheduler=scheduler_)) + return CompositeDisposable( + disp, source.subscribe(observer, scheduler=scheduler_) + ) return Observable(subscribe) return take_with_time + + +__all__ = ["take_with_time_"] diff --git a/rx/core/operators/throttlefirst.py b/rx/core/operators/throttlefirst.py index 5b8360fb3..c78519c57 100644 --- a/rx/core/operators/throttlefirst.py +++ b/rx/core/operators/throttlefirst.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def throttle_first( +def throttle_first_( window_duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def throttle_first(source: Observable[_T]) -> Observable[_T]: @@ -54,4 +54,4 @@ def on_next(x: _T) -> None: return throttle_first -__all__ = ["throttle_first"] +__all__ = ["throttle_first_"] diff --git a/rx/core/operators/timeinterval.py b/rx/core/operators/timeinterval.py index 6969b1dd1..aec6122a9 100644 --- a/rx/core/operators/timeinterval.py +++ b/rx/core/operators/timeinterval.py @@ -13,7 +13,7 @@ class TimeInterval(NamedTuple): interval: timedelta -def time_interval( +def time_interval_( scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: def time_interval(source: Observable[_T]) -> Observable[_T]: diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index 318fb7859..998b6821d 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -14,7 +14,7 @@ _T = TypeVar("_T") -def timeout( +def timeout_( duetime: typing.AbsoluteOrRelativeTime, other: Optional[Union[Observable[_T], "Future[_T]"]] = None, scheduler: Optional[abc.SchedulerBase] = None, diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index f8e453dab..f2f27ed20 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -11,7 +11,7 @@ _T = TypeVar("_T") -def timeout_with_mapper( +def timeout_with_mapper_( first_timeout: Optional[Observable[_T]] = None, timeout_duration_mapper: Optional[Callable[[Any], Observable[Any]]] = None, other: Optional[Observable[_T]] = None, @@ -123,4 +123,4 @@ def on_completed() -> None: return timeout_with_mapper -__all__ = ["timeout_with_mapper"] +__all__ = ["timeout_with_mapper_"] diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index 64a432967..af4ea232a 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -11,7 +11,7 @@ class Timestamp(NamedTuple): timestamp: datetime -def timestamp( +def timestamp_( scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[Any]], Observable[Timestamp]]: def timestamp(source: Observable[Any]) -> Observable[Timestamp]: @@ -43,4 +43,4 @@ def factory(scheduler_: Optional[abc.SchedulerBase] = None): return timestamp -__all__ = ["timestamp"] +__all__ = ["timestamp_"] diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index 1d2172a5d..444749381 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -8,7 +8,7 @@ _TValue = TypeVar("_TValue") -def to_dict( +def to_dict_( key_mapper: Mapper[_T, _TKey], element_mapper: Optional[Mapper[_T, _TValue]] = None ) -> Callable[[Observable[_T]], Observable[Dict[_TKey, _TValue]]]: def to_dict(source: Observable[_T]) -> Observable[Dict[_TKey, _TValue]]: @@ -60,4 +60,4 @@ def on_completed() -> None: return to_dict -__all__ = ["to_dict"] +__all__ = ["to_dict_"] diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index d21581a6a..114d93e45 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def to_future( +def to_future_( future_ctor: Optional[Callable[[], "Future[_T]"]] = None, scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], "Future[_T]"]: @@ -61,4 +61,4 @@ def on_completed(): return to_future -__all__ = ["to_future"] +__all__ = ["to_future_"] diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index 3db3a67d0..07925b97e 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -6,7 +6,7 @@ _T = TypeVar("_T") -def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: +def to_iterable_() -> Callable[[Observable[_T]], Observable[List[_T]]]: def to_iterable(source: Observable[_T]) -> Observable[List[_T]]: """Creates an iterable from an observable sequence. @@ -42,4 +42,4 @@ def on_completed(): return to_iterable -__all__ = ["to_iterable"] +__all__ = ["to_iterable_"] diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index a3264773e..ecc979725 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: +def to_set_() -> Callable[[Observable[_T]], Observable[Set[_T]]]: """Converts the observable sequence to a set. Returns an observable sequence with a single value of a set @@ -32,4 +32,4 @@ def on_completed() -> None: return to_set -__all__ = ["to_set"] +__all__ = ["to_set_"] diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index 1ef7b3a58..57b0ab4ab 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -10,7 +10,7 @@ _T = TypeVar("_T") -def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def while_do_(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: """Repeats source as long as condition holds emulating a while loop. @@ -33,4 +33,4 @@ def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: return while_do -__all__ = ["while_do"] +__all__ = ["while_do_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index d1e5c9a50..1495aea1f 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -784,13 +784,13 @@ def do(observer: abc.ObserverBase[_T]) -> Callable[[Observable[_T]], Observable[ returns the source sequence with the side-effecting behavior applied. """ - from rx.core.operators.do import do as do_ + from rx.core.operators.do import do_ return do_(observer) def do_action( - on_next: Optional[typing.OnNext] = None, + on_next: Optional[typing.OnNext[_T]] = None, on_error: Optional[typing.OnError] = None, on_completed: Optional[typing.OnCompleted] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: @@ -825,9 +825,9 @@ def do_action( returns the source sequence with the side-effecting behavior applied. """ - from rx.core.operators.do import _do_action + from rx.core.operators.do import do_action_ - return _do_action(on_next, on_error, on_completed) + return do_action_(on_next, on_error, on_completed) def do_while(condition: Predicate) -> Callable[[Observable[_T]], Observable[_T]]: @@ -979,9 +979,9 @@ def filter(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T returns an observable sequence that contains elements from the input sequence that satisfy the condition. """ - from rx.core.operators.filter import _filter + from rx.core.operators.filter import filter_ - return _filter(predicate) + return filter_(predicate) def filter_indexed( @@ -1010,9 +1010,9 @@ def filter_indexed( returns an observable sequence that contains elements from the input sequence that satisfy the condition. """ - from rx.core.operators.filter import _filter_indexed + from rx.core.operators.filter import filter_indexed_ - return _filter_indexed(predicate_indexed) + return filter_indexed_(predicate_indexed) def finally_action(action: Callable) -> Callable[[Observable], Observable]: @@ -1099,7 +1099,9 @@ def find_index(predicate: Predicate) -> Callable[[Observable], Observable]: return _find_value(predicate, True) -def first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Observable]: +def first( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the first element of an observable sequence that satisfies the condition in the predicate if present else the first item in the sequence. @@ -1126,9 +1128,9 @@ def first(predicate: Optional[Predicate] = None) -> Callable[[Observable], Obser observable sequence that satisfies the condition in the predicate if provided, else the first item in the sequence. """ - from rx.core.operators.first import _first + from rx.core.operators.first import first_ - return _first(predicate) + return first_(predicate) def first_or_default( @@ -1214,7 +1216,7 @@ def flat_map( def flat_map_indexed( - mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None, + mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, ) -> Callable[[Observable[_T1]], Observable[_T2]]: """The `flat_map_indexed` operator. @@ -1257,7 +1259,9 @@ def flat_map_indexed( return flat_map_indexed_(mapper_indexed) -def flat_map_latest(mapper: Mapper) -> Callable[[Observable], Observable]: +def flat_map_latest( + mapper: Mapper[_T1, Observable[_T2]] +) -> Callable[[Observable[_T1]], Observable[_T2]]: """Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's index and then transforms an observable sequence of observable @@ -1436,7 +1440,7 @@ def group_join( return _group_join(right, left_duration_mapper, right_duration_mapper) -def ignore_elements() -> Callable[[Observable], Observable]: +def ignore_elements() -> Callable[[Observable[_T]], Observable[_T]]: """Ignores all elements in an observable sequence leaving only the termination messages. @@ -1452,9 +1456,9 @@ def ignore_elements() -> Callable[[Observable], Observable]: returns an empty observable sequence that signals termination, successful or exceptional, of the source sequence. """ - from rx.core.operators.ignoreelements import _ignore_elements + from rx.core.operators.ignoreelements import ignore_elements_ - return _ignore_elements() + return ignore_elements_() def is_empty() -> Callable[[Observable[Any]], Observable[bool]]: @@ -1473,9 +1477,9 @@ def is_empty() -> Callable[[Observable[Any]], Observable[bool]]: returns an observable sequence containing a single element determining whether the source sequence is empty. """ - from rx.core.operators.isempty import _is_empty + from rx.core.operators.isempty import is_empty_ - return _is_empty() + return is_empty_() def join( @@ -1543,9 +1547,9 @@ def last( the observable sequence that satisfies the condition in the predicate. """ - from rx.core.operators.last import _last + from rx.core.operators.last import last_ - return _last(predicate) + return last_(predicate) def last_or_default( @@ -1854,12 +1858,12 @@ def min_by( def multicast( - subject: Optional[abc.SubjectBase] = None, + subject: Optional[abc.SubjectBase[_T]] = None, subject_factory: Optional[ - Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase] + Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_T]] ] = None, - mapper: Optional[Callable[[ConnectableObservable], Observable]] = None, -) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: + mapper: Optional[Callable[[ConnectableObservable[_T]], Observable[_T]]] = None, +) -> Callable[[Observable[_T]], Union[Observable[_T], ConnectableObservable[_T]]]: """Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a mapper function. Each subscription to the resulting sequence causes a @@ -2071,8 +2075,8 @@ def pluck_attr(prop: str) -> Callable[[Observable], Observable]: def publish( - mapper: Optional[Mapper] = None, -) -> Callable[[Observable], ConnectableObservable]: + mapper: Optional[Mapper[_T1, _T2]] = None, +) -> Callable[[Observable[_T2]], ConnectableObservable[_T2]]: """The `publish` operator. Returns an observable sequence that is the result of invoking the @@ -2098,9 +2102,9 @@ def publish( sequence produced by multicasting the source sequence within a mapper function. """ - from rx.core.operators.publish import _publish + from rx.core.operators.publish import publish_ - return _publish(mapper) + return publish_(mapper) def publish_value( @@ -2174,9 +2178,9 @@ def reduce( source and returns an observable sequence containing a single element with the final accumulator value. """ - from rx.core.operators.reduce import reduce + from rx.core.operators.reduce import reduce_ - return reduce(accumulator, seed) + return reduce_(accumulator, seed) def ref_count() -> Callable[[ConnectableObservable[_T]], Observable[_T]]: @@ -2216,9 +2220,9 @@ def repeat( returns an observable sequence producing the elements of the given sequence repeatedly. """ - from rx.core.operators.repeat import repeat + from rx.core.operators.repeat import repeat_ - return repeat(repeat_count) + return repeat_(repeat_count) def replay( @@ -2285,9 +2289,9 @@ def retry( An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully. """ - from rx.core.operators.retry import retry + from rx.core.operators.retry import retry_ - return retry(retry_count) + return retry_(retry_count) def sample( @@ -2317,9 +2321,9 @@ def sample( An operator function that takes an observable source and returns a sampled observable sequence. """ - from rx.core.operators.sample import sample + from rx.core.operators.sample import sample_ - return sample(sampler, scheduler) + return sample_(sampler, scheduler) def scan( @@ -2353,9 +2357,9 @@ def scan( source and returns an observable sequence containing the accumulated values. """ - from rx.core.operators.scan import _scan + from rx.core.operators.scan import scan_ - return _scan(accumulator, seed) + return scan_(accumulator, seed) def sequence_equal( @@ -2390,9 +2394,9 @@ def sequence_equal( their corresponding elements are equal according to the specified equality comparer. """ - from rx.core.operators.sequenceequal import _sequence_equal + from rx.core.operators.sequenceequal import _sequence_equal_ - return _sequence_equal(second, comparer) + return _sequence_equal_(second, comparer) def share() -> Callable[[Observable[_T]], Observable[_T]]: @@ -2409,9 +2413,9 @@ def share() -> Callable[[Observable[_T]], Observable[_T]]: source Observable. """ - from rx.core.operators.publish import share + from rx.core.operators.publish import share_ - return share() + return share_() def single( @@ -2444,9 +2448,9 @@ def single( the observable sequence that satisfies the condition in the predicate. """ - from rx.core.operators.single import single + from rx.core.operators.single import single_ - return single(predicate) + return single_(predicate) def single_or_default( @@ -2482,17 +2486,17 @@ def single_or_default( the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists. """ - from rx.core.operators.singleordefault import single_or_default + from rx.core.operators.singleordefault import single_or_default_ - return single_or_default(predicate, default_value) + return single_or_default_(predicate, default_value) def single_or_default_async( has_default: bool = False, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: - from rx.core.operators.singleordefault import single_or_default_async + from rx.core.operators.singleordefault import single_or_default_async_ - return single_or_default_async(has_default, default_value) + return single_or_default_async_(has_default, default_value) def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2518,9 +2522,9 @@ def skip(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence that contains the elements that occur after the specified index in the input sequence. """ - from rx.core.operators.skip import skip + from rx.core.operators.skip import skip_ - return skip(count) + return skip_(count) def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2551,9 +2555,9 @@ def skip_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: returns an observable sequence containing the source sequence elements except for the bypassed ones at the end. """ - from rx.core.operators.skiplast import skip_last + from rx.core.operators.skiplast import skip_last_ - return skip_last(count) + return skip_last_(count) def skip_last_with_time( @@ -2580,9 +2584,9 @@ def skip_last_with_time( An observable sequence with the elements skipped during the specified duration from the end of the source sequence. """ - from rx.core.operators.skiplastwithtime import skip_last_with_time + from rx.core.operators.skiplastwithtime import skip_last_with_time_ - return skip_last_with_time(duration, scheduler=scheduler) + return skip_last_with_time_(duration, scheduler=scheduler) def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -2607,9 +2611,9 @@ def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[ source sequence starting from the point the other sequence triggered propagation. """ - from rx.core.operators.skipuntil import skip_until + from rx.core.operators.skipuntil import skip_until_ - return skip_until(other) + return skip_until_(other) def skip_until_with_time( @@ -2642,9 +2646,9 @@ def skip_until_with_time( returns an observable sequence with the elements skipped until the specified start time. """ - from rx.core.operators.skipuntilwithtime import _skip_until_with_time + from rx.core.operators.skipuntilwithtime import skip_until_with_time_ - return _skip_until_with_time(start_time, scheduler=scheduler) + return skip_until_with_time_(start_time, scheduler=scheduler) def skip_while( @@ -2677,9 +2681,9 @@ def skip_while( the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. """ - from rx.core.operators.skipwhile import skip_while + from rx.core.operators.skipwhile import skip_while_ - return skip_while(predicate) + return skip_while_(predicate) def skip_while_indexed( @@ -2711,9 +2715,9 @@ def skip_while_indexed( the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. """ - from rx.core.operators.skipwhile import skip_while_indexed + from rx.core.operators.skipwhile import skip_while_indexed_ - return skip_while_indexed(predicate) + return skip_while_indexed_(predicate) def skip_with_time( @@ -2750,9 +2754,9 @@ def skip_with_time( returns an observable sequence with the elements skipped during the specified duration from the start of the source sequence. """ - from rx.core.operators.skipwithtime import _skip_with_time + from rx.core.operators.skipwithtime import skip_with_time_ - return _skip_with_time(duration, scheduler=scheduler) + return skip_with_time_(duration, scheduler=scheduler) def slice( @@ -2788,9 +2792,9 @@ def slice( An operator function that takes an observable source and returns a sliced observable sequence. """ - from rx.core.operators.slice import _slice + from rx.core.operators.slice import slice_ - return _slice(start, stop, step) + return slice_(start, stop, step) def some( @@ -2823,9 +2827,9 @@ def some( pass the test in the specified predicate if given, else if some items are in the sequence. """ - from rx.core.operators.some import _some + from rx.core.operators.some import some_ - return _some(predicate) + return some_(predicate) def starmap( @@ -2917,9 +2921,9 @@ def start_with(*args: Any) -> Callable[[Observable], Observable]: An operator function that takes a source observable and returns the source sequence prepended with the specified values. """ - from rx.core.operators.startswith import start_with + from rx.core.operators.startswith import start_with_ - return start_with(*args) + return start_with_(*args) def subscribe_on( @@ -2945,9 +2949,9 @@ def subscribe_on( returns the source sequence whose subscriptions and un-subscriptions happen on the specified scheduler. """ - from rx.core.operators.subscribeon import subscribe_on + from rx.core.operators.subscribeon import subscribe_on_ - return subscribe_on(scheduler) + return subscribe_on_(scheduler) def sum( @@ -2978,9 +2982,9 @@ def sum( an observable sequence containing a single element with the sum of the values in the source sequence. """ - from rx.core.operators.sum import _sum + from rx.core.operators.sum import sum_ - return _sum(key_mapper) + return sum_(key_mapper) def switch_latest() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: @@ -3101,9 +3105,9 @@ def take_last_buffer(count: int) -> Callable[[Observable[_T]], Observable[_T]]: the specified number of elements from the end of the source sequence. """ - from rx.core.operators.takelastbuffer import _take_last_buffer + from rx.core.operators.takelastbuffer import take_last_buffer_ - return _take_last_buffer(count) + return take_last_buffer_(count) def take_last_with_time( @@ -3305,9 +3309,9 @@ def take_with_time( returns an observable sequence with the elements taken during the specified duration from the start of the source sequence. """ - from rx.core.operators.takewithtime import _take_with_time + from rx.core.operators.takewithtime import take_with_time_ - return _take_with_time(duration, scheduler=scheduler) + return take_with_time_(duration, scheduler=scheduler) def throttle_first( @@ -3325,9 +3329,9 @@ def throttle_first( An operator function that takes an observable source and returns an observable that performs the throttle operation. """ - from rx.core.operators.throttlefirst import throttle_first + from rx.core.operators.throttlefirst import throttle_first_ - return throttle_first(window_duration, scheduler) + return throttle_first_(window_duration, scheduler) def throttle_with_mapper( @@ -3373,9 +3377,9 @@ def timestamp( source and returns an observable sequence with timestamp information on values. """ - from rx.core.operators.timestamp import timestamp + from rx.core.operators.timestamp import timestamp_ - return timestamp(scheduler=scheduler) + return timestamp_(scheduler=scheduler) def timeout( @@ -3412,9 +3416,9 @@ def timeout( returns the source sequence switching to the other sequence in case of a timeout. """ - from rx.core.operators.timeout import timeout + from rx.core.operators.timeout import timeout_ - return timeout(duetime, other, scheduler) + return timeout_(duetime, other, scheduler) def timeout_with_mapper( @@ -3445,9 +3449,9 @@ def timeout_with_mapper( returns the source sequence switching to the other sequence in case of a timeout. """ - from rx.core.operators.timeoutwithmapper import timeout_with_mapper + from rx.core.operators.timeoutwithmapper import timeout_with_mapper_ - return timeout_with_mapper(first_timeout, timeout_duration_mapper, other) + return timeout_with_mapper_(first_timeout, timeout_duration_mapper, other) def time_interval( @@ -3471,9 +3475,9 @@ def time_interval( returns an observable sequence with time interval information on values. """ - from rx.core.operators.timeinterval import time_interval + from rx.core.operators.timeinterval import time_interval_ - return time_interval(scheduler=scheduler) + return time_interval_(scheduler=scheduler) def to_dict( @@ -3493,9 +3497,9 @@ def to_dict( returns an observable sequence with a single value of a dictionary containing the values from the observable sequence. """ - from rx.core.operators.todict import to_dict + from rx.core.operators.todict import to_dict_ - return to_dict(key_mapper, element_mapper) + return to_dict_(key_mapper, element_mapper) def to_future( @@ -3513,9 +3517,9 @@ def to_future( An operator function that takes an observable source and returns a future with the last value from the observable sequence. """ - from rx.core.operators.tofuture import to_future + from rx.core.operators.tofuture import to_future_ - return to_future(future_ctor) + return to_future_(future_ctor) def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: @@ -3528,9 +3532,9 @@ def to_iterable() -> Callable[[Observable[_T]], Observable[List[_T]]]: returns an observable sequence containing a single element with an iterable containing all the elements of the source sequence. """ - from rx.core.operators.toiterable import to_iterable + from rx.core.operators.toiterable import to_iterable_ - return to_iterable() + return to_iterable_() to_list = to_iterable @@ -3563,9 +3567,9 @@ def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: returns an observable sequence with a single value of a set containing the values from the observable sequence. """ - from rx.core.operators.toset import to_set + from rx.core.operators.toset import to_set_ - return to_set() + return to_set_() def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -3581,9 +3585,9 @@ def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[ returns an observable sequence which is repeated as long as the condition holds. """ - from rx.core.operators.whiledo import while_do + from rx.core.operators.whiledo import while_do_ - return while_do(condition) + return while_do_(condition) def window( From fef77e7c63c2e94f1c46cb58fb3c12405c34de91 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 5 Feb 2022 17:30:51 +0100 Subject: [PATCH 018/103] isort --- rx/core/__init__.py | 2 +- rx/core/abc/disposable.py | 2 +- rx/core/observable/concat.py | 16 ++++++++++++---- rx/core/observable/defer.py | 2 +- rx/core/observable/ifthen.py | 2 +- rx/core/observable/merge.py | 1 + rx/core/observable/onerrorresumenext.py | 2 +- rx/core/observable/repeat.py | 2 +- rx/core/observable/start.py | 2 +- rx/core/observable/toasync.py | 2 +- rx/core/observable/withlatestfrom.py | 3 ++- rx/core/operators/amb.py | 2 +- rx/core/operators/asobservable.py | 2 +- rx/core/operators/contains.py | 2 +- rx/core/operators/delay.py | 7 +++++-- rx/core/operators/delaywithmapper.py | 7 +++++-- rx/core/operators/dematerialize.py | 2 +- rx/core/operators/expand.py | 7 +++++-- rx/core/operators/flatmap.py | 2 +- rx/core/operators/groupbyuntil.py | 7 +++++-- rx/core/operators/groupjoin.py | 7 +++++-- rx/core/operators/materialize.py | 3 +-- rx/core/operators/maxby.py | 2 +- rx/core/operators/merge.py | 2 +- rx/core/operators/minby.py | 4 ++-- rx/core/operators/observeon.py | 1 - rx/core/operators/onerrorresumenext.py | 4 +--- rx/core/operators/replay.py | 16 +++++++++++----- rx/core/operators/sample.py | 2 +- rx/core/operators/scan.py | 2 +- rx/core/operators/sequenceequal.py | 4 ++-- rx/core/operators/singleordefault.py | 2 +- rx/core/operators/skiplastwithtime.py | 2 +- rx/core/operators/skipwithtime.py | 2 +- rx/core/operators/slice.py | 2 +- rx/core/operators/subscribeon.py | 2 +- rx/core/operators/takelastbuffer.py | 2 +- rx/core/operators/takelastwithtime.py | 2 +- rx/core/operators/takeuntil.py | 2 +- rx/core/operators/throttlefirst.py | 2 +- rx/core/operators/todict.py | 2 +- rx/core/operators/toiterable.py | 1 - rx/core/operators/tomarbles.py | 2 +- rx/core/operators/toset.py | 2 +- rx/core/operators/whiledo.py | 2 +- rx/core/operators/zip.py | 2 +- rx/internal/__init__.py | 7 +++++-- rx/internal/basic.py | 2 +- rx/internal/concurrency.py | 4 ++-- rx/internal/utils.py | 2 +- rx/operators/__init__.py | 6 +++--- rx/scheduler/eventloop/asyncioscheduler.py | 3 +-- rx/scheduler/eventloop/eventletscheduler.py | 3 +-- rx/scheduler/eventloop/geventscheduler.py | 7 ++++--- rx/scheduler/eventloop/ioloopscheduler.py | 3 +-- rx/scheduler/eventloop/twistedscheduler.py | 3 +-- rx/scheduler/mainloop/gtkscheduler.py | 3 +-- rx/scheduler/mainloop/qtscheduler.py | 3 +-- rx/scheduler/mainloop/tkinterscheduler.py | 3 +-- rx/scheduler/mainloop/wxscheduler.py | 3 +-- rx/testing/__init__.py | 3 +-- setup.cfg | 5 ++++- 62 files changed, 117 insertions(+), 93 deletions(-) diff --git a/rx/core/__init__.py b/rx/core/__init__.py index 140260298..c59a2820e 100644 --- a/rx/core/__init__.py +++ b/rx/core/__init__.py @@ -1,8 +1,8 @@ from . import abc +from .notification import Notification from .observable import ConnectableObservable, GroupedObservable, Observable from .observer import Observer from .pipe import pipe -from .notification import Notification __all__ = [ "abc", diff --git a/rx/core/abc/disposable.py b/rx/core/abc/disposable.py index 250c06daf..94b05480a 100644 --- a/rx/core/abc/disposable.py +++ b/rx/core/abc/disposable.py @@ -1,4 +1,4 @@ -from abc import abstractmethod, ABC +from abc import ABC, abstractmethod from types import TracebackType from typing import Optional, Type diff --git a/rx/core/observable/concat.py b/rx/core/observable/concat.py index a4e4b8971..e07f55c05 100644 --- a/rx/core/observable/concat.py +++ b/rx/core/observable/concat.py @@ -1,15 +1,21 @@ from typing import Any, Iterable, Optional, TypeVar from rx.core import Observable, abc -from rx.disposable import (CompositeDisposable, Disposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + Disposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.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: + def subscribe( + observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: _scheduler = scheduler_ or CurrentThreadScheduler.singleton() sources_ = iter(sources) @@ -35,7 +41,9 @@ def on_completed(): else: d = SingleAssignmentDisposable() subscription.disposable = d - d.disposable = current.subscribe_(observer.on_next, observer.on_error, on_completed, scheduler_) + d.disposable = current.subscribe_( + observer.on_next, observer.on_error, on_completed, scheduler_ + ) cancelable.disposable = _scheduler.schedule(action) diff --git a/rx/core/observable/defer.py b/rx/core/observable/defer.py index 3c299b64a..06a24ccec 100644 --- a/rx/core/observable/defer.py +++ b/rx/core/observable/defer.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, Optional, Union, TypeVar +from typing import Callable, Optional, TypeVar, Union from rx import from_future, throw from rx.core import Observable, abc diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index bfb3dcaeb..9332b853e 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, Union, TypeVar +from typing import Callable, TypeVar, Union import rx from rx.core import Observable, abc diff --git a/rx/core/observable/merge.py b/rx/core/observable/merge.py index fdddc79fc..a67d4f6a0 100644 --- a/rx/core/observable/merge.py +++ b/rx/core/observable/merge.py @@ -1,4 +1,5 @@ from typing import TypeVar + import rx from rx import operators as ops from rx.core import Observable diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 0c2c66960..f9dae7909 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, Optional, Union, TypeVar, Any +from typing import Any, Callable, Optional, TypeVar, Union import rx from rx.core import Observable, abc diff --git a/rx/core/observable/repeat.py b/rx/core/observable/repeat.py index cd26e1766..81e8694bd 100644 --- a/rx/core/observable/repeat.py +++ b/rx/core/observable/repeat.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Optional +from typing import Optional, TypeVar import rx from rx import operators as ops diff --git a/rx/core/observable/start.py b/rx/core/observable/start.py index 4c16e9544..efb9e9789 100644 --- a/rx/core/observable/start.py +++ b/rx/core/observable/start.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Callable, Optional +from typing import Callable, Optional, TypeVar from rx import to_async from rx.core import Observable, abc diff --git a/rx/core/observable/toasync.py b/rx/core/observable/toasync.py index c78fd0e02..0221f86ea 100644 --- a/rx/core/observable/toasync.py +++ b/rx/core/observable/toasync.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Any +from typing import Any, Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, abc diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index 47ae464e8..4a659c7db 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -1,4 +1,5 @@ -from typing import Optional, Tuple, Any, List +from typing import Any, List, Optional, Tuple + from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.utils import NotSet diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index fbd647818..ed783e2a5 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, List, Optional, Union, cast, TypeVar +from typing import Callable, List, Optional, TypeVar, Union, cast from rx import from_future from rx.core import Observable, abc diff --git a/rx/core/operators/asobservable.py b/rx/core/operators/asobservable.py index 0c0b58083..04482358c 100644 --- a/rx/core/operators/asobservable.py +++ b/rx/core/operators/asobservable.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar, Optional +from typing import Callable, Optional, TypeVar from rx.core import Observable, abc diff --git a/rx/core/operators/contains.py b/rx/core/operators/contains.py index 7d2bbe522..06d32c0ac 100644 --- a/rx/core/operators/contains.py +++ b/rx/core/operators/contains.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe, typing diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 4f62c95cc..1b963fa5b 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -3,8 +3,11 @@ from rx import operators as ops from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, MultipleAssignmentDisposable, - SerialDisposable) +from rx.disposable import ( + CompositeDisposable, + MultipleAssignmentDisposable, + SerialDisposable, +) from rx.internal.constants import DELTA_ZERO from rx.scheduler import TimeoutScheduler diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index 2881d8678..b895ba08e 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -1,8 +1,11 @@ from typing import Callable from rx.core import Observable, abc, typing -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) def _delay_with_mapper(subscription_delay=None, delay_duration_mapper=None) -> Callable[[Observable], Observable]: diff --git a/rx/core/operators/dematerialize.py b/rx/core/operators/dematerialize.py index 8b4784080..7e1bf27db 100644 --- a/rx/core/operators/dematerialize.py +++ b/rx/core/operators/dematerialize.py @@ -1,6 +1,6 @@ from typing import Callable, Optional, TypeVar -from rx.core import Observable, Notification, abc +from rx.core import Notification, Observable, abc _T = TypeVar("_T") diff --git a/rx/core/operators/expand.py b/rx/core/operators/expand.py index c7ec15cb6..22eda9eb7 100644 --- a/rx/core/operators/expand.py +++ b/rx/core/operators/expand.py @@ -2,8 +2,11 @@ from rx.core import Observable from rx.core.typing import Mapper -from rx.disposable import (CompositeDisposable, SerialDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + SerialDisposable, + SingleAssignmentDisposable, +) from rx.scheduler import ImmediateScheduler diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index 32f62da65..8e023dd61 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Iterable +from typing import Callable, Iterable, Optional, TypeVar from rx import from_, from_future from rx import operators as ops diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index a4b7a9136..76849e923 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -4,8 +4,11 @@ from rx import operators as ops from rx.core import GroupedObservable, Observable from rx.core.typing import Mapper -from rx.disposable import (CompositeDisposable, RefCountDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + RefCountDisposable, + SingleAssignmentDisposable, +) from rx.internal.basic import identity from rx.subject import Subject diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index fa4395ac5..658397037 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -4,8 +4,11 @@ from rx import operators as ops from rx.core import Observable -from rx.disposable import (CompositeDisposable, RefCountDisposable, - SingleAssignmentDisposable) +from rx.disposable import ( + CompositeDisposable, + RefCountDisposable, + SingleAssignmentDisposable, +) from rx.internal.utils import add_ref from rx.subject import Subject diff --git a/rx/core/operators/materialize.py b/rx/core/operators/materialize.py index c4fbd38d4..1351cbeef 100644 --- a/rx/core/operators/materialize.py +++ b/rx/core/operators/materialize.py @@ -1,8 +1,7 @@ from typing import Callable, Optional, TypeVar -from rx.core.notification import Notification from rx.core import Observable, abc -from rx.core.notification import OnCompleted, OnError, OnNext +from rx.core.notification import Notification, OnCompleted, OnError, OnNext _T = TypeVar("_T") diff --git a/rx/core/operators/maxby.py b/rx/core/operators/maxby.py index 1c89ffe61..d7c2920b3 100644 --- a/rx/core/operators/maxby.py +++ b/rx/core/operators/maxby.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, List +from typing import Callable, List, Optional, TypeVar from rx.core import Observable, typing from rx.internal.basic import default_sub_comparer diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index a99fdd3fd..af6633176 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -2,7 +2,7 @@ import rx from rx import from_future -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.concurrency import synchronized from rx.internal.utils import is_future diff --git a/rx/core/operators/minby.py b/rx/core/operators/minby.py index 259c55f37..2978ab86f 100644 --- a/rx/core/operators/minby.py +++ b/rx/core/operators/minby.py @@ -1,6 +1,6 @@ -from typing import Callable, Optional, TypeVar, cast, List +from typing import Callable, List, Optional, TypeVar, cast -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.internal.basic import default_sub_comparer _T = TypeVar("_T") diff --git a/rx/core/operators/observeon.py b/rx/core/operators/observeon.py index 0574571f8..ec2217a2c 100644 --- a/rx/core/operators/observeon.py +++ b/rx/core/operators/observeon.py @@ -3,7 +3,6 @@ from rx.core import Observable, abc from rx.core.observer import ObserveOnObserver - _T = TypeVar("_T") diff --git a/rx/core/operators/onerrorresumenext.py b/rx/core/operators/onerrorresumenext.py index 50506a381..368a3c2e2 100644 --- a/rx/core/operators/onerrorresumenext.py +++ b/rx/core/operators/onerrorresumenext.py @@ -1,6 +1,4 @@ -from typing import Callable - -from typing import TypeVar +from typing import Callable, TypeVar import rx from rx.core import Observable diff --git a/rx/core/operators/replay.py b/rx/core/operators/replay.py index e6eccbb42..a7fd921d1 100644 --- a/rx/core/operators/replay.py +++ b/rx/core/operators/replay.py @@ -1,17 +1,20 @@ -from typing import Callable, Optional, Union +from typing import Callable, Optional, TypeVar, Union from rx import operators as ops from rx.core import ConnectableObservable, Observable, abc, typing from rx.core.typing import Mapper from rx.subject import ReplaySubject +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") -def _replay( - mapper: Optional[Mapper] = None, + +def replay_( + mapper: Optional[Mapper[_T1, _T2]] = None, buffer_size: Optional[int] = None, window: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T2]]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence replaying notifications @@ -45,9 +48,12 @@ def _replay( if mapper: - def subject_factory(scheduler): + def subject_factory(scheduler: abc.SchedulerBase) -> ReplaySubject[_T2]: return ReplaySubject(buffer_size, window, scheduler) return ops.multicast(subject_factory=subject_factory, mapper=mapper) return ops.multicast(ReplaySubject(buffer_size, window, scheduler)) + + +__all__ = ["replay_"] diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index d1d513664..f7a5e70f6 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Union, Any, cast +from typing import Any, Callable, Optional, TypeVar, Union, cast import rx from rx.core import Observable, abc, typing diff --git a/rx/core/operators/scan.py b/rx/core/operators/scan.py index d4d19d6d3..271d2cdad 100644 --- a/rx/core/operators/scan.py +++ b/rx/core/operators/scan.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar, Union, Type, cast +from typing import Callable, Type, TypeVar, Union, cast from rx import defer from rx import operators as ops diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index b563cf4ee..e2545d29b 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -1,7 +1,7 @@ -from typing import Callable, List, Optional, TypeVar, Iterable +from typing import Callable, Iterable, List, Optional, TypeVar import rx -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable from rx.internal import default_comparer diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index d49f4b2e0..909aa5da3 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -1,7 +1,7 @@ from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, pipe, abc +from rx.core import Observable, abc, pipe from rx.core.typing import Predicate from rx.internal.exceptions import SequenceContainsNoElementsError diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index 9721b6be1..586b21e44 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict, List, Optional, TypeVar, Any +from typing import Any, Callable, Dict, List, Optional, TypeVar from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler diff --git a/rx/core/operators/skipwithtime.py b/rx/core/operators/skipwithtime.py index c73616062..45a869517 100644 --- a/rx/core/operators/skipwithtime.py +++ b/rx/core/operators/skipwithtime.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Any +from typing import Any, Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable diff --git a/rx/core/operators/slice.py b/rx/core/operators/slice.py index 9b629a28a..de5d90eac 100644 --- a/rx/core/operators/slice.py +++ b/rx/core/operators/slice.py @@ -1,5 +1,5 @@ from sys import maxsize -from typing import Callable, Optional, TypeVar, Any, List +from typing import Any, Callable, List, Optional, TypeVar from rx import operators as ops from rx.core import Observable diff --git a/rx/core/operators/subscribeon.py b/rx/core/operators/subscribeon.py index 72d5a0234..4b5ccefc7 100644 --- a/rx/core/operators/subscribeon.py +++ b/rx/core/operators/subscribeon.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar, Optional, Any +from typing import Any, Callable, Optional, TypeVar from rx.core import Observable, abc from rx.disposable import ( diff --git a/rx/core/operators/takelastbuffer.py b/rx/core/operators/takelastbuffer.py index 4126ce981..c29c0762f 100644 --- a/rx/core/operators/takelastbuffer.py +++ b/rx/core/operators/takelastbuffer.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, List +from typing import Callable, List, Optional, TypeVar from rx.core import Observable, abc diff --git a/rx/core/operators/takelastwithtime.py b/rx/core/operators/takelastwithtime.py index e8481a483..2f3acc6ae 100644 --- a/rx/core/operators/takelastwithtime.py +++ b/rx/core/operators/takelastwithtime.py @@ -1,4 +1,4 @@ -from typing import Callable, List, Optional, TypeVar, Dict, Any +from typing import Any, Callable, Dict, List, Optional, TypeVar from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index 19375deb3..93f675138 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, Optional, Union, TypeVar +from typing import Callable, Optional, TypeVar, Union from rx import from_future from rx.core import Observable, abc diff --git a/rx/core/operators/throttlefirst.py b/rx/core/operators/throttlefirst.py index c78519c57..6d7c99250 100644 --- a/rx/core/operators/throttlefirst.py +++ b/rx/core/operators/throttlefirst.py @@ -1,5 +1,5 @@ -from typing import Callable, Optional, TypeVar from datetime import datetime +from typing import Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.scheduler import TimeoutScheduler diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index 444749381..2ea8ba3e0 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -1,4 +1,4 @@ -from typing import cast, Callable, Optional, TypeVar, Dict +from typing import Callable, Dict, Optional, TypeVar, cast from rx.core import Observable, abc from rx.core.typing import Mapper diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index 07925b97e..2c29172a2 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -2,7 +2,6 @@ from rx.core import Observable, abc - _T = TypeVar("_T") diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index 2d1beace3..9149fdf7c 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Any +from typing import Any, List, Optional from rx.core import Observable, abc from rx.core.typing import RelativeTime diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index ecc979725..a055d2c36 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar, Set, Optional +from typing import Callable, Optional, Set, TypeVar from rx.core import Observable, abc diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index 57b0ab4ab..3714a3cb5 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -1,6 +1,6 @@ import itertools from asyncio import Future -from typing import Callable, Union, TypeVar +from typing import Callable, TypeVar, Union import rx from rx.core import Observable diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index 20b29677e..e407b9ddc 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -1,4 +1,4 @@ -from typing import Callable, Iterable, Any, Optional, Tuple, TypeVar +from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar import rx from rx.core import Observable, abc diff --git a/rx/internal/__init__.py b/rx/internal/__init__.py index ffbc729fd..a75aa8fb2 100644 --- a/rx/internal/__init__.py +++ b/rx/internal/__init__.py @@ -1,5 +1,8 @@ from . import concurrency, constants from .basic import default_comparer, default_error, noop -from .exceptions import (ArgumentOutOfRangeException, DisposedException, - SequenceContainsNoElementsError) +from .exceptions import ( + ArgumentOutOfRangeException, + DisposedException, + SequenceContainsNoElementsError, +) from .priorityqueue import PriorityQueue diff --git a/rx/internal/basic.py b/rx/internal/basic.py index 34348a205..6ddf5e68a 100644 --- a/rx/internal/basic.py +++ b/rx/internal/basic.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, NoReturn, Union, TypeVar +from typing import Any, NoReturn, TypeVar, Union _T = TypeVar("_T") diff --git a/rx/internal/concurrency.py b/rx/internal/concurrency.py index fdcf57d8e..ba121f6ac 100644 --- a/rx/internal/concurrency.py +++ b/rx/internal/concurrency.py @@ -1,5 +1,5 @@ -from threading import Thread, RLock -from typing import Callable, Any +from threading import RLock, Thread +from typing import Any, Callable from rx.core.typing import StartableTarget diff --git a/rx/internal/utils.py b/rx/internal/utils.py index 7bc6dbf19..817866e08 100644 --- a/rx/internal/utils.py +++ b/rx/internal/utils.py @@ -1,7 +1,7 @@ from asyncio import Future from functools import update_wrapper from types import FunctionType -from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, cast, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, TypeVar, cast from rx.core import abc from rx.disposable import CompositeDisposable diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 1495aea1f..274f7d911 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -18,11 +18,11 @@ from rx.core import ( ConnectableObservable, GroupedObservable, + Notification, Observable, abc, pipe, typing, - Notification, ) from rx.core.typing import ( Accumulator, @@ -2265,9 +2265,9 @@ def replay( sequence produced by multicasting the source sequence within a mapper function. """ - from rx.core.operators.replay import _replay + from rx.core.operators.replay import replay_ - return _replay(mapper, buffer_size, window, scheduler=scheduler) + return replay_(mapper, buffer_size, window, scheduler=scheduler) def retry( diff --git a/rx/scheduler/eventloop/asyncioscheduler.py b/rx/scheduler/eventloop/asyncioscheduler.py index 01803838c..da4fe2d74 100644 --- a/rx/scheduler/eventloop/asyncioscheduler.py +++ b/rx/scheduler/eventloop/asyncioscheduler.py @@ -4,8 +4,7 @@ from typing import Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/eventloop/eventletscheduler.py b/rx/scheduler/eventloop/eventletscheduler.py index d2a58739e..e06a49bde 100644 --- a/rx/scheduler/eventloop/eventletscheduler.py +++ b/rx/scheduler/eventloop/eventletscheduler.py @@ -3,8 +3,7 @@ from typing import Any, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/eventloop/geventscheduler.py b/rx/scheduler/eventloop/geventscheduler.py index face23f5f..c68bad92e 100644 --- a/rx/scheduler/eventloop/geventscheduler.py +++ b/rx/scheduler/eventloop/geventscheduler.py @@ -3,8 +3,7 @@ from typing import Any, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler @@ -29,7 +28,9 @@ def __init__(self, gevent: Any) -> None: super().__init__() self._gevent = gevent - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/eventloop/ioloopscheduler.py b/rx/scheduler/eventloop/ioloopscheduler.py index 8fe230e4b..46c801998 100644 --- a/rx/scheduler/eventloop/ioloopscheduler.py +++ b/rx/scheduler/eventloop/ioloopscheduler.py @@ -3,8 +3,7 @@ from typing import Any, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/eventloop/twistedscheduler.py b/rx/scheduler/eventloop/twistedscheduler.py index 2e65ec578..2ff73717e 100644 --- a/rx/scheduler/eventloop/twistedscheduler.py +++ b/rx/scheduler/eventloop/twistedscheduler.py @@ -3,8 +3,7 @@ from typing import Any, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/mainloop/gtkscheduler.py b/rx/scheduler/mainloop/gtkscheduler.py index d5ec4b016..88ce19712 100644 --- a/rx/scheduler/mainloop/gtkscheduler.py +++ b/rx/scheduler/mainloop/gtkscheduler.py @@ -1,8 +1,7 @@ from typing import Any, Optional, TypeVar, cast from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/mainloop/qtscheduler.py b/rx/scheduler/mainloop/qtscheduler.py index f7cf1710e..70da7e246 100644 --- a/rx/scheduler/mainloop/qtscheduler.py +++ b/rx/scheduler/mainloop/qtscheduler.py @@ -3,8 +3,7 @@ from typing import Any, Optional, Set, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/mainloop/tkinterscheduler.py b/rx/scheduler/mainloop/tkinterscheduler.py index 75bd43fd1..bf38ba130 100644 --- a/rx/scheduler/mainloop/tkinterscheduler.py +++ b/rx/scheduler/mainloop/tkinterscheduler.py @@ -1,8 +1,7 @@ from typing import Any, Optional, TypeVar from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/mainloop/wxscheduler.py b/rx/scheduler/mainloop/wxscheduler.py index 0e3923b15..aaf92fa6b 100644 --- a/rx/scheduler/mainloop/wxscheduler.py +++ b/rx/scheduler/mainloop/wxscheduler.py @@ -2,8 +2,7 @@ from typing import Any, Optional, Set, TypeVar, cast from rx.core import abc, typing -from rx.disposable import (CompositeDisposable, Disposable, - SingleAssignmentDisposable) +from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable from ..periodicscheduler import PeriodicScheduler diff --git a/rx/testing/__init__.py b/rx/testing/__init__.py index 392457e77..185a49ae8 100644 --- a/rx/testing/__init__.py +++ b/rx/testing/__init__.py @@ -1,5 +1,4 @@ from .mockdisposable import MockDisposable -from .reactivetest import (OnErrorPredicate, OnNextPredicate, ReactiveTest, - is_prime) +from .reactivetest import OnErrorPredicate, OnNextPredicate, ReactiveTest, is_prime from .recorded import Recorded from .testscheduler import TestScheduler diff --git a/setup.cfg b/setup.cfg index a323191d8..ba013ab6f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,7 @@ test = pytest testpaths = tests [flake8] -max-line-length = 120 \ No newline at end of file +max-line-length = 120 + +[isort] +profile = black \ No newline at end of file From ae608fbaebfa252f32095bd3501e9ee9f034037c Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 06:40:16 +0100 Subject: [PATCH 019/103] Fixed more operators --- rx/core/operators/distinctuntilchanged.py | 51 +++++++++++++++-------- rx/core/operators/elementatordefault.py | 31 ++++++++++---- rx/core/operators/exclusive.py | 38 ++++++++++++----- rx/core/operators/find.py | 39 +++++++++++------ rx/operators/__init__.py | 44 +++++++++++-------- 5 files changed, 137 insertions(+), 66 deletions(-) diff --git a/rx/core/operators/distinctuntilchanged.py b/rx/core/operators/distinctuntilchanged.py index 5092086b3..6b266c0d0 100644 --- a/rx/core/operators/distinctuntilchanged.py +++ b/rx/core/operators/distinctuntilchanged.py @@ -1,19 +1,22 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, cast -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Comparer, Mapper from rx.internal.basic import default_comparer, identity +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") -def _distinct_until_changed( - key_mapper: Optional[Mapper] = None, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: - key_mapper = key_mapper or identity +def distinct_until_changed_( + key_mapper: Optional[Mapper[_T, _TKey]] = None, + comparer: Optional[Comparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + + key_mapper = key_mapper or cast(Callable[[_T], _TKey], identity) comparer = comparer or default_comparer - def distinct_until_changed(source: Observable) -> Observable: + def distinct_until_changed(source: Observable[_T]) -> Observable[_T]: """Returns an observable sequence that contains only distinct contiguous elements according to the key_mapper and the comparer. @@ -36,11 +39,16 @@ def distinct_until_changed(source: Observable) -> Observable: contiguous elements, based on a computed key value, from the source sequence. """ - def subscribe(observer, scheduler=None): - has_current_key = [False] - current_key = [None] - def on_next(value): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + has_current_key = False + current_key: Optional[_TKey] = None + + def on_next(value: _T) -> None: + nonlocal has_current_key, current_key comparer_equals = False try: key = key_mapper(value) @@ -48,18 +56,25 @@ def on_next(value): observer.on_error(exception) return - if has_current_key[0]: + if has_current_key: try: - comparer_equals = comparer(current_key[0], key) + comparer_equals = comparer(current_key, key) except Exception as exception: # pylint: disable=broad-except observer.on_error(exception) return - if not has_current_key[0] or not comparer_equals: - has_current_key[0] = True - current_key[0] = key + if not has_current_key or not comparer_equals: + has_current_key = True + current_key = key observer.on_next(value) - return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler=scheduler) + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler + ) + return Observable(subscribe) + return distinct_until_changed + + +__all__ = ["distinct_until_changed_"] diff --git a/rx/core/operators/elementatordefault.py b/rx/core/operators/elementatordefault.py index d73560ce2..c4a41d3e0 100644 --- a/rx/core/operators/elementatordefault.py +++ b/rx/core/operators/elementatordefault.py @@ -1,18 +1,26 @@ -from typing import Any, Callable +from typing import Callable, Optional, TypeVar, cast -from rx.core import Observable +from rx.core import Observable, abc from rx.internal.exceptions import ArgumentOutOfRangeException -def _element_at_or_default(index, has_default=False, default_value=None): +_T = TypeVar("_T") + + +def element_at_or_default_( + index: int, has_default: bool = False, default_value: Optional[_T] = None +) -> Callable[[Observable[_T]], Observable[_T]]: if index < 0: raise ArgumentOutOfRangeException() - def element_at_or_default(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def element_at_or_default(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: i = [index] - def on_next(x): + def on_next(x: _T) -> None: found = False with source.lock: if i[0]: @@ -28,9 +36,16 @@ def on_completed(): if not has_default: observer.on_error(ArgumentOutOfRangeException()) else: - observer.on_next(default_value) + observer.on_next(cast(_T, default_value)) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) + return element_at_or_default + + +__all__ = ["element_at_or_default_"] diff --git a/rx/core/operators/exclusive.py b/rx/core/operators/exclusive.py index 41b796aee..4f4f433fc 100644 --- a/rx/core/operators/exclusive.py +++ b/rx/core/operators/exclusive.py @@ -1,12 +1,14 @@ -from typing import Callable +from asyncio import Future +from typing import Callable, Optional, TypeVar, Union import rx -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -from rx.internal.utils import is_future +_T = TypeVar("_T") -def _exclusive() -> Callable[[Observable], Observable]: + +def exclusive_() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: """Performs a exclusive waiting for the first to finish before subscribing to another observable. Observables that come in between subscriptions will be dropped on the floor. @@ -16,8 +18,11 @@ def _exclusive() -> Callable[[Observable], Observable]: happen when subscribed. """ - def exclusive(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def exclusive(source: Observable[Observable[_T]]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: has_current = [False] is_stopped = [False] m = SingleAssignmentDisposable() @@ -25,11 +30,15 @@ def subscribe(observer, scheduler=None): g.add(m) - def on_next(inner_source): + def on_next(inner_source: Union[Observable[_T], "Future[_T]"]) -> None: if not has_current[0]: has_current[0] = True - inner_source = rx.from_future(inner_source) if is_future(inner_source) else inner_source + inner_source = ( + rx.from_future(inner_source) + if isinstance(inner_source, Future) + else inner_source + ) inner_subscription = SingleAssignmentDisposable() g.add(inner_subscription) @@ -44,15 +53,22 @@ def on_completed_inner(): observer.on_next, observer.on_error, on_completed_inner, - scheduler + scheduler, ) - def on_completed(): + def on_completed() -> None: is_stopped[0] = True if not has_current[0] and len(g) == 1: observer.on_completed() - m.disposable = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + m.disposable = source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) return g + return Observable(subscribe) + return exclusive + + +__all__ = ["exclusive_"] diff --git a/rx/core/operators/find.py b/rx/core/operators/find.py index ddb2822f5..3425e9c61 100644 --- a/rx/core/operators/find.py +++ b/rx/core/operators/find.py @@ -1,32 +1,47 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar, Union -from rx.core import Observable -from rx.core.typing import Predicate +from rx.core import Observable, abc -def _find_value(predicate: Predicate, yield_index) -> Callable[[Observable], Observable]: - def find_value(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - i = [0] +_T = TypeVar("_T") - def on_next(x): + +def find_value_( + predicate: Callable[[_T, int, Observable[_T]], bool], yield_index: bool +) -> Callable[[Observable[_T]], Observable[Union[_T, int, None]]]: + def find_value(source: Observable[_T]) -> Observable[Union[_T, int, None]]: + def subscribe( + observer: abc.ObserverBase[Union[_T, int, None]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + index = 0 + + def on_next(x: _T) -> None: + nonlocal index should_run = False try: - should_run = predicate(x, i, source) + should_run = predicate(x, index, source) except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) return if should_run: - observer.on_next(i[0] if yield_index else x) + observer.on_next(index if yield_index else x) observer.on_completed() else: - i[0] += 1 + index += 1 def on_completed(): observer.on_next(-1 if yield_index else None) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) + return Observable(subscribe) + return find_value + + +__all__ = ["find_value_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 274f7d911..1345a6b43 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -754,9 +754,9 @@ def distinct_until_changed( contiguous elements, based on a computed key value, from the source sequence. """ - from rx.core.operators.distinctuntilchanged import _distinct_until_changed + from rx.core.operators.distinctuntilchanged import distinct_until_changed_ - return _distinct_until_changed(key_mapper, comparer) + return distinct_until_changed_(key_mapper, comparer) def do(observer: abc.ObserverBase[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -873,16 +873,16 @@ def element_at(index: int) -> Callable[[Observable[_T]], Observable[_T]]: Returns: An operator function that takes an observable source and - returns an observable sequence that produces the element at + returns an observable sequence that produces the element at the specified position in the source sequence. """ - from rx.core.operators.elementatordefault import _element_at_or_default + from rx.core.operators.elementatordefault import element_at_or_default_ - return _element_at_or_default(index, False) + return element_at_or_default_(index, False) def element_at_or_default( - index: int, default_value: Any = None + index: int, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the element at a specified index in a sequence or a default value if the index is out of range. @@ -909,12 +909,12 @@ def element_at_or_default( specified position in the source sequence, or a default value if the index is outside the bounds of the source sequence. """ - from rx.core.operators.elementatordefault import _element_at_or_default + from rx.core.operators.elementatordefault import element_at_or_default_ - return _element_at_or_default(index, True, default_value) + return element_at_or_default_(index, True, default_value) -def exclusive() -> Callable[[Observable[_T]], Observable[_T]]: +def exclusive() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: """Performs a exclusive waiting for the first to finish before subscribing to another observable. Observables that come in between subscriptions will be dropped on the floor. @@ -934,9 +934,9 @@ def exclusive() -> Callable[[Observable[_T]], Observable[_T]]: An exclusive observable with only the results that happen when subscribed. """ - from rx.core.operators.exclusive import _exclusive + from rx.core.operators.exclusive import exclusive_ - return _exclusive() + return exclusive_() def expand(mapper: Mapper) -> Callable[[Observable[_T]], Observable[_T]]: @@ -1044,7 +1044,9 @@ def finally_action(action: Callable) -> Callable[[Observable], Observable]: return _finally_action(action) -def find(predicate: Predicate) -> Callable[[Observable], Observable]: +def find( + predicate: Callable[[_T, int, Observable[_T]], bool] +) -> Callable[[Observable[_T]], Observable[Union[_T, None]]]: """Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire Observable sequence. @@ -1066,12 +1068,17 @@ def find(predicate: Predicate) -> Callable[[Observable], Observable]: matches the conditions defined by the specified predicate, if found otherwise, None. """ - from rx.core.operators.find import _find_value + from rx.core.operators.find import find_value_ - return _find_value(predicate, False) + return cast( + Callable[[Observable[_T]], Observable[Union[_T, None]]], + find_value_(predicate, False), + ) -def find_index(predicate: Predicate) -> Callable[[Observable], Observable]: +def find_index( + predicate: Callable[[_T, int, Observable[_T]], bool] +) -> Callable[[Observable[_T]], Observable[Union[int, None]]]: """Searches for an element that matches the conditions defined by the specified predicate, and returns an Observable sequence with the zero-based index of the first occurrence within the entire @@ -1094,9 +1101,12 @@ def find_index(predicate: Predicate) -> Callable[[Observable], Observable]: first occurrence of an element that matches the conditions defined by match, if found; otherwise, -1. """ - from rx.core.operators.find import _find_value + from rx.core.operators.find import find_value_ - return _find_value(predicate, True) + return cast( + Callable[[Observable[_T]], Observable[Union[int, None]]], + find_value_(predicate, True), + ) def first( From 739596d2b37f9cc3e6611cdeae268130f2c69827 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 07:33:41 +0100 Subject: [PATCH 020/103] Type fixes --- rx/__init__.py | 4 +-- rx/core/observable/using.py | 22 ++++++++++---- rx/core/operators/merge.py | 9 +++--- rx/core/operators/multicast.py | 40 +++++++++++++++++------- rx/core/operators/sequenceequal.py | 4 +-- rx/core/operators/timestamp.py | 14 ++++++--- rx/operators/__init__.py | 49 ++++++++++++++++++------------ rx/subject/asyncsubject.py | 8 +++-- rx/subject/behaviorsubject.py | 20 +++++++----- rx/subject/replaysubject.py | 12 +++++--- 10 files changed, 118 insertions(+), 64 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 7ab8345fa..fb3ca565d 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1185,9 +1185,9 @@ def using( An observable sequence whose lifetime controls the lifetime of the dependent resource object. """ - from .core.observable.using import _using + from .core.observable.using import using_ - return _using(resource_factory, observable_factory) + return using_(resource_factory, observable_factory) def with_latest_from(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: diff --git a/rx/core/observable/using.py b/rx/core/observable/using.py index fc6b9d17d..762c1b9d0 100644 --- a/rx/core/observable/using.py +++ b/rx/core/observable/using.py @@ -1,13 +1,16 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar import rx from rx.core import Observable, abc from rx.disposable import CompositeDisposable, Disposable +_T = TypeVar("_T") -def _using( - resource_factory: Callable[[], abc.DisposableBase], observable_factory: Callable[[abc.DisposableBase], Observable] -) -> Observable: + +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. @@ -25,7 +28,9 @@ def _using( of the dependent resource object. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: disp = Disposable() try: @@ -38,6 +43,11 @@ def subscribe(observer, scheduler=None): d = rx.throw(exception).subscribe(observer, scheduler=scheduler) return CompositeDisposable(d, disp) - return CompositeDisposable(source.subscribe(observer, scheduler=scheduler), disp) + return CompositeDisposable( + source.subscribe(observer, scheduler=scheduler), disp + ) return Observable(subscribe) + + +__all__ = ["using_"] diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index af6633176..efe527c39 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -1,11 +1,11 @@ -from typing import Callable, List, Optional, TypeVar +from asyncio import Future +from typing import Callable, List, Optional, TypeVar, Union import rx from rx import from_future from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.concurrency import synchronized -from rx.internal.utils import is_future _T = TypeVar("_T") @@ -65,6 +65,7 @@ def on_completed(): ) def on_next(inner_source: Observable[_T]) -> None: + assert max_concurrent if active_count[0] < max_concurrent: active_count[0] += 1 subscribe(inner_source) @@ -110,13 +111,13 @@ def subscribe( m = SingleAssignmentDisposable() group.add(m) - def on_next(inner_source: Observable[_T]): + def on_next(inner_source: Union[Observable[_T], "Future[_T]"]): inner_subscription = SingleAssignmentDisposable() group.add(inner_subscription) inner_source = ( from_future(inner_source) - if is_future(inner_source) + if isinstance(inner_source, Future) else inner_source ) diff --git a/rx/core/operators/multicast.py b/rx/core/operators/multicast.py index 5edbaf102..a66e0cf0a 100644 --- a/rx/core/operators/multicast.py +++ b/rx/core/operators/multicast.py @@ -1,14 +1,18 @@ -from typing import Callable, Optional, Union +from typing import Callable, Optional, TypeVar, Union from rx.core import ConnectableObservable, Observable, abc -from rx.core.typing import Mapper from rx.disposable import CompositeDisposable +_T = TypeVar("_T") -def _multicast(subject: Optional[abc.SubjectBase] = None, - subject_factory: Optional[Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase]] = None, - mapper: Optional[Callable[[ConnectableObservable], Observable]] = None - ) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: + +def multicast_( + subject: Optional[abc.SubjectBase[_T]] = None, + subject_factory: Optional[ + Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_T]] + ] = None, + mapper: Optional[Callable[[ConnectableObservable[_T]], Observable[_T]]] = None, +) -> Callable[[Observable[_T]], Union[Observable[_T], ConnectableObservable[_T]]]: """Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a mapper function. Each subscription to the resulting sequence causes a @@ -36,13 +40,27 @@ def _multicast(subject: Optional[abc.SubjectBase] = None, function. """ - def multicast(source: Observable) -> Union[Observable, ConnectableObservable]: + def multicast( + source: Observable[_T], + ) -> Union[Observable[_T], ConnectableObservable[_T]]: if subject_factory: - def subscribe(observer, scheduler=None): - connectable = source.pipe(_multicast(subject=subject_factory(scheduler))) - subscription = mapper(connectable).subscribe(observer, scheduler=scheduler) + + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + connectable = source.pipe( + multicast_(subject=subject_factory(scheduler)) + ) + assert mapper + subscription = mapper(connectable).subscribe( + observer, scheduler=scheduler + ) return CompositeDisposable(subscription, connectable.connect(scheduler)) + return Observable(subscribe) - return ConnectableObservable(source, subject) + ret: ConnectableObservable[_T] = ConnectableObservable(source, subject) + return ret + return multicast diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index e2545d29b..3bc6d437a 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -8,7 +8,7 @@ _T = TypeVar("_T") -def _sequence_equal_( +def sequence_equal_( second: Observable[_T], comparer: Optional[typing.Comparer[_T]] = None ) -> Callable[[Observable[_T]], Observable[bool]]: comparer = comparer or default_comparer @@ -116,4 +116,4 @@ def on_completed2(): return sequence_equal -__all__ = ["_sequence_equal_"] +__all__ = ["sequence_equal_"] diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index af4ea232a..11be0d69f 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -1,20 +1,24 @@ +from dataclasses import dataclass from datetime import datetime -from typing import Any, Callable, NamedTuple, Optional +from typing import Any, Callable, Optional, TypeVar, Generic from rx import defer, operators from rx.core import Observable, abc from rx.scheduler import TimeoutScheduler +_T = TypeVar("_T") -class Timestamp(NamedTuple): - value: Any + +@dataclass +class Timestamp(Generic[_T]): + value: _T timestamp: datetime def timestamp_( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[Any]], Observable[Timestamp]]: - def timestamp(source: Observable[Any]) -> Observable[Timestamp]: +) -> Callable[[Observable[_T]], Observable[Timestamp[_T]]]: + def timestamp(source: Observable[Any]) -> Observable[Timestamp[_T]]: """Records the timestamp for each value in an observable sequence. Examples: diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 1345a6b43..9646d0cef 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2,6 +2,7 @@ from asyncio import Future from typing import ( + TYPE_CHECKING, Any, Callable, Dict, @@ -10,6 +11,7 @@ Optional, Set, Tuple, + Type, TypeVar, Union, cast, @@ -32,6 +34,7 @@ Predicate, PredicateIndexed, ) +from rx.internal.basic import identity from rx.internal.utils import NotSet from rx.subject import Subject @@ -1901,9 +1904,9 @@ def multicast( sequence produced by multicasting the source sequence within a mapper function. """ - from rx.core.operators.multicast import _multicast + from rx.core.operators.multicast import multicast_ - return _multicast(subject, subject_factory, mapper) + return multicast_(subject, subject_factory, mapper) def observe_on( @@ -2047,7 +2050,9 @@ def partition_indexed( return partition_indexed_(predicate_indexed) -def pluck(key: Any) -> Callable[[Observable], Observable]: +def pluck( + key: _TKey, +) -> Callable[[Observable[Dict[_TKey, _TValue]]], Observable[_TValue]]: """Retrieves the value of a specified key using dict-like access (as in element[key]) from all elements in the Observable sequence. @@ -2337,7 +2342,7 @@ def sample( def scan( - accumulator: Accumulator[_TState, _T], seed: Union[_TState, NotSet] = NotSet + accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """The scan operator. @@ -2373,8 +2378,8 @@ def scan( def sequence_equal( - second: Observable, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: + second: Observable[_T], comparer: Optional[Comparer[_T]] = None +) -> Callable[[Observable[_T]], Observable[bool]]: """Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer. @@ -2404,9 +2409,9 @@ def sequence_equal( their corresponding elements are equal according to the specified equality comparer. """ - from rx.core.operators.sequenceequal import _sequence_equal_ + from rx.core.operators.sequenceequal import sequence_equal_ - return _sequence_equal_(second, comparer) + return sequence_equal_(second, comparer) def share() -> Callable[[Observable[_T]], Observable[_T]]: @@ -2502,7 +2507,7 @@ def single_or_default( def single_or_default_async( - has_default: bool = False, default_value: Optional[_T] = None + has_default: bool = False, default_value: _T = None ) -> Callable[[Observable[_T]], Observable[_T]]: from rx.core.operators.singleordefault import single_or_default_async_ @@ -2877,14 +2882,14 @@ def starmap( """ if mapper is None: - return pipe() + return pipe(identity) return pipe(map(lambda values: cast(Mapper, mapper)(*values))) def starmap_indexed( - mapper: Optional[Callable[..., _T]] = None -) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: + mapper: Optional[Callable[..., Any]] = None +) -> Callable[[Observable[Tuple[Any, ...]]], Observable[Any]]: """Variant of :func:`starmap` which accepts an indexed mapper. .. marble:: @@ -2909,12 +2914,12 @@ def starmap_indexed( """ if mapper is None: - return pipe() + return pipe(identity) - return pipe(map(lambda values: cast(MapperIndexed, mapper)(*values))) + return pipe(map(lambda values: cast(MapperIndexed[Any, Any], mapper)(*values))) -def start_with(*args: Any) -> Callable[[Observable], Observable]: +def start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: """Prepends a sequence of values to an observable sequence. .. marble:: @@ -3084,7 +3089,7 @@ def take_last(count: int) -> Callable[[Observable[_T]], Observable[_T]]: return take_last_(count) -def take_last_buffer(count: int) -> Callable[[Observable[_T]], Observable[_T]]: +def take_last_buffer(count: int) -> Callable[[Observable[_T]], Observable[List[_T]]]: """The `take_last_buffer` operator. Returns an array with the specified number of contiguous elements @@ -3254,8 +3259,8 @@ def take_while( def take_while_indexed( - predicate: PredicateIndexed, inclusive: bool = False -) -> Callable[[Observable], Observable]: + predicate: PredicateIndexed[_T], inclusive: bool = False +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. @@ -3290,7 +3295,7 @@ def take_while_indexed( def take_with_time( duration: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Takes elements for the specified duration from the start of the observable source sequence. @@ -3369,9 +3374,13 @@ def throttle_with_mapper( return throttle_with_mapper(throttle_duration_mapper) +if TYPE_CHECKING: + from rx.core.operators.timestamp import Timestamp + + def timestamp( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[Any]]: +) -> Callable[[Observable[_T]], Observable["Timestamp[_T]"]]: """The timestamp operator. Records the timestamp for each value in an observable sequence. diff --git a/rx/subject/asyncsubject.py b/rx/subject/asyncsubject.py index ca8d26ba0..5569a824d 100644 --- a/rx/subject/asyncsubject.py +++ b/rx/subject/asyncsubject.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, TypeVar +from typing import Any, Optional, TypeVar, cast from rx.core import abc, typing from rx.disposable import Disposable @@ -20,11 +20,13 @@ def __init__(self) -> None: super().__init__() - self.value: Optional[_T] = None + self.value: _T = cast(_T, None) self.has_value: bool = False def _subscribe_core( - self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: with self.lock: self.check_disposed() diff --git a/rx/subject/behaviorsubject.py b/rx/subject/behaviorsubject.py index 2f742d5da..39f0e8dde 100644 --- a/rx/subject/behaviorsubject.py +++ b/rx/subject/behaviorsubject.py @@ -1,18 +1,20 @@ -from typing import Any +from typing import Any, Optional, TypeVar from rx.disposable import Disposable - +from rx.core import abc from .innersubscription import InnerSubscription from .subject import Subject +_T = TypeVar("_T") + -class BehaviorSubject(Subject): +class BehaviorSubject(Subject[_T]): """Represents a value that changes over time. Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications. """ - def __init__(self, value) -> None: + def __init__(self, value: _T) -> None: """Initializes a new instance of the BehaviorSubject class which creates a subject that caches its last value and starts with the specified value. @@ -24,9 +26,13 @@ def __init__(self, value) -> None: super().__init__() - self.value = value + self.value: _T = value - def _subscribe_core(self, observer, scheduler=None): + def _subscribe_core( + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: with self.lock: self.check_disposed() if not self.is_stopped: @@ -42,7 +48,7 @@ def _subscribe_core(self, observer, scheduler=None): return Disposable() - def _on_next_core(self, value: Any) -> None: + def _on_next_core(self, value: _T) -> None: """Notifies all subscribed observers with the value.""" with self.lock: observers = self.observers.copy() diff --git a/rx/subject/replaysubject.py b/rx/subject/replaysubject.py index 3206f5230..02837886e 100644 --- a/rx/subject/replaysubject.py +++ b/rx/subject/replaysubject.py @@ -52,11 +52,15 @@ def __init__( super().__init__() self.buffer_size = sys.maxsize if buffer_size is None else buffer_size self.scheduler = scheduler or CurrentThreadScheduler.singleton() - self.window = timedelta.max if window is None else self.scheduler.to_timedelta(window) + self.window = ( + timedelta.max if window is None else self.scheduler.to_timedelta(window) + ) self.queue: List[QueueItem] = [] def _subscribe_core( - self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: so = ScheduledObserver(self.scheduler, observer) subscription = RemovableDisposable(self, so) @@ -77,14 +81,14 @@ def _subscribe_core( so.ensure_active() return subscription - def _trim(self, now: datetime): + def _trim(self, now: datetime) -> None: while len(self.queue) > self.buffer_size: self.queue.pop(0) while self.queue and (now - self.queue[0].interval) > self.window: self.queue.pop(0) - def _on_next_core(self, value: Any) -> None: + def _on_next_core(self, value: _T) -> None: """Notifies all subscribed observers with the value.""" with self.lock: From fe2cf9ac2ab23138ddbeaa280917fdcb7e7651b3 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 10:31:24 +0100 Subject: [PATCH 021/103] Fix test helper classes --- rx/core/operators/finallyaction.py | 23 ++++++++++++++++++----- rx/core/typing.py | 2 +- rx/disposable/__init__.py | 11 +++++++++++ rx/disposable/refcountdisposable.py | 2 +- rx/internal/basic.py | 2 +- rx/operators/__init__.py | 6 +++--- rx/testing/coldobservable.py | 24 +++++++++++++++++------- rx/testing/hotobservable.py | 18 +++++++++++++----- rx/testing/mockdisposable.py | 6 +++--- rx/testing/mockobserver.py | 10 +++++----- rx/testing/recorded.py | 29 +++++++++++++++++++++-------- rx/testing/subscription.py | 21 +++++++++++++-------- 12 files changed, 107 insertions(+), 47 deletions(-) diff --git a/rx/core/operators/finallyaction.py b/rx/core/operators/finallyaction.py index bb6da49bb..2e921dc4c 100644 --- a/rx/core/operators/finallyaction.py +++ b/rx/core/operators/finallyaction.py @@ -1,11 +1,16 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar -from rx.core import Observable +from rx.core import Observable, typing, abc from rx.disposable import Disposable -def _finally_action(action: Callable) -> Callable[[Observable], Observable]: - def finally_action(source: Observable) -> Observable: +_T = TypeVar("_T") + + +def finally_action_( + action: typing.Action, +) -> Callable[[Observable[_T]], Observable[_T]]: + def finally_action(source: Observable[_T]) -> Observable[_T]: """Invokes a specified action after the source observable sequence terminates gracefully or exceptionally. @@ -20,7 +25,10 @@ def finally_action(source: Observable) -> Observable: behavior applied. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: try: subscription = source.subscribe(observer, scheduler=scheduler) except Exception: @@ -34,5 +42,10 @@ def dispose(): action() return Disposable(dispose) + return Observable(subscribe) + return finally_action + + +__all__ = ["finally_action_"] diff --git a/rx/core/typing.py b/rx/core/typing.py index 8cd7e312f..baf10c250 100644 --- a/rx/core/typing.py +++ b/rx/core/typing.py @@ -15,7 +15,7 @@ ) from .abc.startable import StartableBase -_TState = TypeVar("_TState") # Can be anything +_TState = TypeVar("_TState") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") diff --git a/rx/disposable/__init__.py b/rx/disposable/__init__.py index fb0b814d3..a1cbfc758 100644 --- a/rx/disposable/__init__.py +++ b/rx/disposable/__init__.py @@ -6,3 +6,14 @@ from .scheduleddisposable import ScheduledDisposable from .serialdisposable import SerialDisposable from .singleassignmentdisposable import SingleAssignmentDisposable + +__all__ = [ + "BooleanDisposable", + "CompositeDisposable", + "Disposable", + "MultipleAssignmentDisposable", + "RefCountDisposable", + "ScheduledDisposable", + "SerialDisposable", + "SingleAssignmentDisposable", +] diff --git a/rx/disposable/refcountdisposable.py b/rx/disposable/refcountdisposable.py index 5ed8301c5..5a028c0ec 100644 --- a/rx/disposable/refcountdisposable.py +++ b/rx/disposable/refcountdisposable.py @@ -2,7 +2,7 @@ from typing import Optional from rx.core.abc import DisposableBase -from rx.disposable import Disposable +from .disposable import Disposable class RefCountDisposable(DisposableBase): diff --git a/rx/internal/basic.py b/rx/internal/basic.py index 6ddf5e68a..67f26f234 100644 --- a/rx/internal/basic.py +++ b/rx/internal/basic.py @@ -3,7 +3,7 @@ _T = TypeVar("_T") -# Defaults + def noop(*args: Any, **kw: Any): """No operation. Returns nothing""" pass diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 9646d0cef..45766c0b2 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1018,7 +1018,7 @@ def filter_indexed( return filter_indexed_(predicate_indexed) -def finally_action(action: Callable) -> Callable[[Observable], Observable]: +def finally_action(action: typing.Action) -> Callable[[Observable[_T]], Observable[_T]]: """Invokes a specified action after the source observable sequence terminates gracefully or exceptionally. @@ -1042,9 +1042,9 @@ def finally_action(action: Callable) -> Callable[[Observable], Observable]: returns an observable sequence with the action-invoking termination behavior applied. """ - from rx.core.operators.finallyaction import _finally_action + from rx.core.operators.finallyaction import finally_action_ - return _finally_action(action) + return finally_action_(action) def find( diff --git a/rx/testing/coldobservable.py b/rx/testing/coldobservable.py index 6535cf22f..020a7f833 100644 --- a/rx/testing/coldobservable.py +++ b/rx/testing/coldobservable.py @@ -1,30 +1,40 @@ -from typing import List, Optional, TypeVar +from typing import Any, List, Optional, TypeVar -from rx.core import Observable, abc +from rx.core import Observable, abc, Notification from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import VirtualTimeScheduler +from .recorded import Recorded from .subscription import Subscription _T = TypeVar("_T") class ColdObservable(Observable[_T]): - def __init__(self, scheduler: VirtualTimeScheduler, messages) -> None: + def __init__( + self, scheduler: VirtualTimeScheduler, messages: List[Recorded[_T]] + ) -> None: super().__init__() self.scheduler: VirtualTimeScheduler = scheduler self.messages = messages self.subscriptions: List[Subscription] = [] - def _subscribe_core(self, observer=None, scheduler: Optional[abc.SubjectBase] = None) -> abc.DisposableBase: + def _subscribe_core( + self, + observer: Optional[abc.ObserverBase[_T]] = None, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: self.subscriptions.append(Subscription(self.scheduler.clock)) index = len(self.subscriptions) - 1 disp = CompositeDisposable() - def get_action(notification): - def action(scheduler, state): - notification.accept(observer) + def get_action(notification: Notification[_T]) -> abc.ScheduledAction[_T]: + def action( + scheduler: abc.SchedulerBase, state: Any = None + ) -> abc.DisposableBase: + if observer: + notification.accept(observer) return Disposable() return action diff --git a/rx/testing/hotobservable.py b/rx/testing/hotobservable.py index d755c59b5..4d33acf9c 100644 --- a/rx/testing/hotobservable.py +++ b/rx/testing/hotobservable.py @@ -12,7 +12,9 @@ class HotObservable(Observable[_T]): - def __init__(self, scheduler: VirtualTimeScheduler, messages: List[Recorded]) -> None: + def __init__( + self, scheduler: VirtualTimeScheduler, messages: List[Recorded[_T]] + ) -> None: super().__init__() self.scheduler: VirtualTimeScheduler = scheduler @@ -22,7 +24,7 @@ def __init__(self, scheduler: VirtualTimeScheduler, messages: List[Recorded]) -> observable = self - def get_action(notification: Notification): + def get_action(notification: Notification[_T]): def action(scheduler: abc.SchedulerBase, state: Any): for observer in observable.observers[:]: notification.accept(observer) @@ -37,13 +39,19 @@ def action(scheduler: abc.SchedulerBase, state: Any): action = get_action(notification) scheduler.schedule_absolute(message.time, action) - def _subscribe_core(self, observer=None, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: - self.observers.append(observer) + def _subscribe_core( + self, + observer: Optional[abc.ObserverBase[_T]] = None, + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + if observer: + self.observers.append(observer) self.subscriptions.append(Subscription(self.scheduler.clock)) index = len(self.subscriptions) - 1 def dispose_action(): - self.observers.remove(observer) + if observer: + self.observers.remove(observer) start = self.subscriptions[index].subscribe end = self.scheduler.clock self.subscriptions[index] = Subscription(start, end) diff --git a/rx/testing/mockdisposable.py b/rx/testing/mockdisposable.py index cfc718bc8..ff48c31ef 100644 --- a/rx/testing/mockdisposable.py +++ b/rx/testing/mockdisposable.py @@ -1,14 +1,14 @@ from typing import List -from rx.core import typing +from rx.core import typing, abc from rx.scheduler import VirtualTimeScheduler -class MockDisposable: +class MockDisposable(abc.DisposableBase): def __init__(self, scheduler: VirtualTimeScheduler): self.scheduler: VirtualTimeScheduler = scheduler self.disposes: List[typing.AbsoluteTime] = [] self.disposes.append(self.scheduler.clock) - def dispose(self): + def dispose(self) -> None: self.disposes.append(self.scheduler.clock) diff --git a/rx/testing/mockobserver.py b/rx/testing/mockobserver.py index 95643cf63..fe8109d23 100644 --- a/rx/testing/mockobserver.py +++ b/rx/testing/mockobserver.py @@ -1,6 +1,6 @@ -from typing import Any, List, TypeVar +from typing import List, TypeVar -from rx.core.abc import ObserverBase +from rx.core import abc from rx.core.notification import OnCompleted, OnError, OnNext from rx.scheduler import VirtualTimeScheduler @@ -9,12 +9,12 @@ _T = TypeVar("_T") -class MockObserver(ObserverBase[_T]): +class MockObserver(abc.ObserverBase[_T]): def __init__(self, scheduler: VirtualTimeScheduler) -> None: self.scheduler: VirtualTimeScheduler = scheduler - self.messages: List[Recorded] = [] + self.messages: List[Recorded[_T]] = [] - def on_next(self, value: Any) -> None: + def on_next(self, value: _T) -> None: self.messages.append(Recorded(self.scheduler.clock, OnNext(value))) def on_error(self, error: Exception) -> None: diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index fa08ecda3..f3b9af6c8 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,24 +1,37 @@ -from typing import Any +from typing import Any, TypeVar, Generic, Optional, cast +from rx.core import typing, Notification from rx.internal.basic import default_comparer +_T = TypeVar("_T") -class Recorded: - def __init__(self, time: int, value: Any, comparer=None): + +class Recorded(Generic[_T]): + def __init__( + self, + time: int, + value: Notification[_T], + comparer: Optional[typing.Comparer[_T]] = None, + ): self.time = time self.value = value self.comparer = comparer or default_comparer - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """Returns true if a recorded value matches another recorded value""" - time_match = self.time == other.time - return time_match and self.comparer(self.value, other.value) + if isinstance(other, Recorded): + time_match = self.time == other.time + return time_match and self.comparer( + self.value, cast(Recorded[_T], other).value + ) + + return False equals = __eq__ - def __repr__(self): + def __repr__(self) -> str: return str(self) - def __str__(self): + def __str__(self) -> str: return "%s@%s" % (self.value, self.time) diff --git a/rx/testing/subscription.py b/rx/testing/subscription.py index ac00b3e24..88b29ab46 100644 --- a/rx/testing/subscription.py +++ b/rx/testing/subscription.py @@ -1,20 +1,25 @@ import sys +from typing import Any, Optional -class Subscription(object): - def __init__(self, start, end=None): +class Subscription: + def __init__(self, start: int, end: Optional[int] = None): self.subscribe = start self.unsubscribe = end or sys.maxsize - def equals(self, other): - return self.subscribe == other.subscribe and self.unsubscribe == other.unsubscribe + def equals(self, other: Any) -> bool: + return ( + self.subscribe == other.subscribe and self.unsubscribe == other.unsubscribe + ) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return self.equals(other) - def __repr__(self): + def __repr__(self) -> str: return str(self) - def __str__(self): - unsubscribe = "Infinite" if self.unsubscribe == sys.maxsize else self.unsubscribe + def __str__(self) -> str: + unsubscribe = ( + "Infinite" if self.unsubscribe == sys.maxsize else self.unsubscribe + ) return "(%s, %s)" % (self.subscribe, unsubscribe) From 775d8f7a41da005111ceab3ed320f1e3e6fdb50f Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 11:35:35 +0100 Subject: [PATCH 022/103] Fix export of operators --- rx/__init__.py | 8 ++- rx/core/observable/zip.py | 13 +++- rx/operators/__init__.py | 139 +++++++++++++++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 9 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index fb3ca565d..df3c95c36 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -874,7 +874,9 @@ def of(*args: _T) -> Observable[_T]: def on_error_resume_next( - *sources: Union[Observable[_T], "Future[_T]", Callable[[Optional[Exception]], Observable[_T]]] + *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. @@ -1243,9 +1245,9 @@ def zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: An observable sequence containing the result of combining elements of the sources as a :class:`tuple`. """ - from .core.observable.zip import _zip + from .core.observable.zip import zip_ - return _zip(*args) + return zip_(*args) __all__ = [ diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index f00db7ad6..e23f40eba 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -10,7 +10,7 @@ # pylint: disable=redefined-builtin -def _zip(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: +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 @@ -53,13 +53,17 @@ def next(i: int): def func(i: int): source = sources[i] sad = SingleAssignmentDisposable() - source: Observable[Any] = from_future(source) if is_future(source) else source + source: Observable[Any] = ( + from_future(source) if is_future(source) else source + ) def on_next(x: Any): queues[i].append(x) next(i) - sad.disposable = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + sad.disposable = source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) subscriptions[i] = sad for idx in range(n): @@ -67,3 +71,6 @@ def on_next(x: Any): return CompositeDisposable(subscriptions) return Observable(subscribe) + + +__all__ = ["zip_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 45766c0b2..633b19039 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -833,7 +833,7 @@ def do_action( return do_action_(on_next, on_error, on_completed) -def do_while(condition: Predicate) -> Callable[[Observable[_T]], Observable[_T]]: +def do_while(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a do while loop. @@ -2123,8 +2123,8 @@ def publish( def publish_value( - initial_value: Any, mapper: Optional[Mapper] = None -) -> Callable[[Observable], Observable]: + initial_value: _T1, mapper: Optional[Mapper[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence and starts with @@ -3866,3 +3866,136 @@ def zip_with_iterable( zip_with_list = zip_with_iterable + +__all__ = [ + "all", + "amb", + "as_observable", + "average", + "buffer", + "buffer_when", + "buffer_toggle", + "buffer_with_count", + "buffer_with_time", + "buffer_with_time_or_count", + "catch", + "combine_latest", + "concat", + "contains", + "count", + "debounce", + "throttle_with_timeout", + "default_if_empty", + "delay_subscription", + "delay_with_mapper", + "dematerialize", + "delay", + "distinct", + "distinct_until_changed", + "do", + "do_action", + "do_while", + "element_at", + "element_at_or_default", + "exclusive", + "expand", + "filter", + "filter_indexed", + "finally_action", + "find", + "find_index", + "first", + "first_or_default", + "flat_map", + "flat_map_indexed", + "flat_map_latest", + "fork_join", + "group_by", + "group_by_until", + "group_join", + "ignore_elements", + "is_empty", + "join", + "last", + "last_or_default", + "map", + "map_indexed", + "materialize", + "max", + "max_by", + "merge", + "merge_all", + "min", + "min_by", + "multicast", + "observe_on", + "on_error_resume_next", + "pairwise", + "partition", + "partition_indexed", + "pluck", + "pluck_attr", + "publish", + "publish_value", + "reduce", + "ref_count", + "repeat", + "replay", + "retry", + "sample", + "scan", + "sequence_equal", + "share", + "single", + "single_or_default", + "single_or_default_async", + "skip", + "skip_last", + "skip_last_with_time", + "skip_until", + "skip_until_with_time", + "skip_while", + "skip_while_indexed", + "skip_with_time", + "slice", + "some", + "starmap", + "starmap_indexed", + "start_with", + "subscribe_on", + "sum", + "switch_latest", + "take", + "take_last", + "take_last_buffer", + "take_last_with_time", + "take_until", + "take_until_with_time", + "take_while", + "take_while_indexed", + "take_with_time", + "throttle_first", + "throttle_with_mapper", + "timestamp", + "timeout", + "timeout_with_mapper", + "time_interval", + "to_dict", + "to_future", + "to_iterable", + "to_list", + "to_marbles", + "to_set", + "while_do", + "window", + "window_when", + "window_toggle", + "window_with_count", + "window_with_time", + "window_with_time_or_count", + "with_latest_from", + "zip", + "zip_with_list", + "zip_with_iterable", + "zip_with_list", +] From 32f28215fe4da7d9d674357232247819c0619b30 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 11:51:29 +0100 Subject: [PATCH 023/103] Run isort and black - Also add to CI --- .github/workflows/pythonpackage.yml | 4 ++ requirements.txt | 4 +- rx/core/abc/observable.py | 7 ++- rx/core/observable/connectableobservable.py | 11 ++++- rx/core/observable/fromcallback.py | 8 ++- rx/core/observable/generate.py | 7 +-- rx/core/observable/groupedobservable.py | 9 +++- rx/core/observable/marbles.py | 8 ++- rx/core/observable/never.py | 4 +- rx/core/observable/returnvalue.py | 16 ++++-- rx/core/observable/timer.py | 27 +++++++--- rx/core/observable/withlatestfrom.py | 24 ++++++--- rx/core/observer/observeonobserver.py | 1 - rx/core/observer/scheduledobserver.py | 4 +- rx/core/operators/bufferwithtime.py | 5 +- rx/core/operators/bufferwithtimeorcount.py | 6 ++- rx/core/operators/catch.py | 12 +++-- rx/core/operators/delay.py | 23 +++++++-- rx/core/operators/delaysubscription.py | 7 ++- rx/core/operators/delaywithmapper.py | 16 ++++-- rx/core/operators/distinct.py | 13 +++-- rx/core/operators/dowhile.py | 1 + rx/core/operators/elementatordefault.py | 1 - rx/core/operators/expand.py | 7 ++- rx/core/operators/finallyaction.py | 3 +- rx/core/operators/find.py | 1 - rx/core/operators/groupby.py | 14 +++--- rx/core/operators/groupbyuntil.py | 21 +++++--- rx/core/operators/groupjoin.py | 25 +++++++--- rx/core/operators/join.py | 32 ++++++++---- rx/core/operators/last.py | 4 +- rx/core/operators/lastordefault.py | 7 ++- rx/core/operators/publishvalue.py | 5 +- rx/core/operators/skipuntilwithtime.py | 12 +++-- rx/core/operators/skipwithtime.py | 9 +++- rx/core/operators/statistics.py | 30 ++++++----- rx/core/operators/timestamp.py | 2 +- rx/disposable/refcountdisposable.py | 1 + rx/internal/exceptions.py | 8 ++- rx/scheduler/catchscheduler.py | 26 +++++++--- rx/scheduler/currentthreadscheduler.py | 8 ++- rx/scheduler/eventloop/asyncioscheduler.py | 4 +- rx/scheduler/eventloop/eventletscheduler.py | 4 +- rx/scheduler/eventloop/ioloopscheduler.py | 4 +- rx/scheduler/eventloop/twistedscheduler.py | 4 +- rx/scheduler/eventloopscheduler.py | 24 +++++++-- rx/scheduler/historicalscheduler.py | 7 ++- rx/scheduler/immediatescheduler.py | 14 ++++-- rx/scheduler/mainloop/gtkscheduler.py | 8 ++- rx/scheduler/mainloop/pygamescheduler.py | 4 +- rx/scheduler/mainloop/qtscheduler.py | 4 +- rx/scheduler/mainloop/tkinterscheduler.py | 4 +- rx/scheduler/mainloop/wxscheduler.py | 12 +++-- rx/scheduler/newthreadscheduler.py | 30 ++++++++--- rx/scheduler/periodicscheduler.py | 18 +++++-- rx/scheduler/scheduleditem.py | 6 ++- rx/scheduler/scheduler.py | 14 ++++-- rx/scheduler/threadpoolscheduler.py | 4 +- rx/scheduler/trampolinescheduler.py | 14 ++++-- rx/scheduler/virtualtimescheduler.py | 18 +++++-- rx/subject/behaviorsubject.py | 3 +- rx/subject/subject.py | 4 +- rx/testing/coldobservable.py | 2 +- rx/testing/marbles.py | 55 ++++++++++++--------- rx/testing/mockdisposable.py | 2 +- rx/testing/reactivetest.py | 6 +-- rx/testing/recorded.py | 4 +- rx/testing/testscheduler.py | 5 +- setup.cfg | 2 +- 69 files changed, 508 insertions(+), 205 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 8a9c1f30e..4f41defdb 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -19,6 +19,10 @@ jobs: - name: Install dependencies run: | pip install -r requirements.txt + - name: Check formatting + run: | + black --check --verbose rx + isort --check rx - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/requirements.txt b/requirements.txt index 2b323d90d..029062be9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ pytest coveralls -flake8 \ No newline at end of file +flake8 +black +isort \ No newline at end of file diff --git a/rx/core/abc/observable.py b/rx/core/abc/observable.py index 6406d340e..2ca422aab 100644 --- a/rx/core/abc/observable.py +++ b/rx/core/abc/observable.py @@ -16,7 +16,12 @@ class ObservableBase(Generic[_T_out], ABC): __slots__ = () @abstractmethod - def subscribe(self, observer: ObserverBase[_T_out], *, scheduler: Optional[SchedulerBase] = None) -> DisposableBase: + def subscribe( + self, + observer: ObserverBase[_T_out], + *, + scheduler: Optional[SchedulerBase] = None + ) -> DisposableBase: """Subscribe an observer to the observable sequence. Args: diff --git a/rx/core/observable/connectableobservable.py b/rx/core/observable/connectableobservable.py index 09adc08f1..43ddb829e 100644 --- a/rx/core/observable/connectableobservable.py +++ b/rx/core/observable/connectableobservable.py @@ -20,7 +20,11 @@ def __init__(self, source: abc.ObservableBase[_T], subject: abc.SubjectBase[_T]) super().__init__() - def _subscribe_core(self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def _subscribe_core( + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): return self.subject.subscribe(observer, scheduler=scheduler) def connect(self, scheduler: Optional[abc.SchedulerBase] = None): @@ -55,7 +59,10 @@ def auto_connect(self, subscriber_count: int = 1) -> Observable[_T]: connectable_subscription[0] = source.connect() is_connected[0] = True - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ): count[0] += 1 should_connect = count[0] == subscriber_count and not is_connected[0] subscription = source.subscribe(observer) diff --git a/rx/core/observable/fromcallback.py b/rx/core/observable/fromcallback.py index 385e1b959..9abe53714 100644 --- a/rx/core/observable/fromcallback.py +++ b/rx/core/observable/fromcallback.py @@ -5,7 +5,9 @@ from rx.disposable import Disposable -def _from_callback(func: Callable, mapper: Optional[Mapper] = None) -> Callable[[], Observable]: +def _from_callback( + func: Callable, mapper: Optional[Mapper] = None +) -> Callable[[], Observable]: """Converts a callback function to an observable sequence. Args: @@ -23,7 +25,9 @@ def _from_callback(func: Callable, mapper: Optional[Mapper] = None) -> Callable[ def function(*args): arguments = list(args) - def subscribe(observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + def subscribe( + observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: def handler(*args): results = list(args) if mapper: diff --git a/rx/core/observable/generate.py b/rx/core/observable/generate.py index 9bcb3c224..f27837788 100644 --- a/rx/core/observable/generate.py +++ b/rx/core/observable/generate.py @@ -6,11 +6,7 @@ from rx.scheduler import CurrentThreadScheduler -def _generate(initial_state: Any, - condition: Predicate, - iterate: Mapper - ) -> Observable: - +def _generate(initial_state: Any, condition: Predicate, iterate: Mapper) -> Observable: def subscribe(observer, scheduler=None): scheduler = scheduler or CurrentThreadScheduler.singleton() first = True @@ -46,4 +42,5 @@ def action(scheduler, state1=None): mad.disposable = scheduler.schedule(action) return mad + return Observable(subscribe) diff --git a/rx/core/observable/groupedobservable.py b/rx/core/observable/groupedobservable.py index 314555938..c2679faac 100644 --- a/rx/core/observable/groupedobservable.py +++ b/rx/core/observable/groupedobservable.py @@ -9,9 +9,14 @@ def __init__(self, key, underlying_observable, merged_disposable=None): self.key = key def subscribe(observer, scheduler=None): - return CompositeDisposable(merged_disposable.disposable, underlying_observable.subscribe(observer, scheduler=scheduler)) + return CompositeDisposable( + merged_disposable.disposable, + underlying_observable.subscribe(observer, scheduler=scheduler), + ) - self.underlying_observable = underlying_observable if not merged_disposable else Observable(subscribe) + self.underlying_observable = ( + underlying_observable if not merged_disposable else Observable(subscribe) + ) def _subscribe_core(self, observer, scheduler=None): return self.underlying_observable.subscribe(observer, scheduler=scheduler) diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index 5522b9cc0..6ed2af286 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -107,7 +107,9 @@ def from_marbles( scheduler: Optional[SchedulerBase] = None, ) -> Observable: - messages = parse(string, timespan=timespan, lookup=lookup, error=error, raise_stopped=True) + messages = parse( + string, timespan=timespan, lookup=lookup, error=error, raise_stopped=True + ) def subscribe(observer, scheduler_): _scheduler = scheduler or scheduler_ or new_thread_scheduler @@ -247,7 +249,9 @@ def check_stopped(element): elements = group[1:-1].split(",") for elm in elements: check_stopped(elm) - grp_messages = [map_element(timestamp, elm) for elm in elements if elm != ""] + grp_messages = [ + map_element(timestamp, elm) for elm in elements if elm != "" + ] messages.extend(grp_messages) iframe += len(group) diff --git a/rx/core/observable/never.py b/rx/core/observable/never.py index c514d6d41..473f3e17d 100644 --- a/rx/core/observable/never.py +++ b/rx/core/observable/never.py @@ -12,7 +12,9 @@ def never_() -> Observable[Any]: An observable sequence whose observers will never get called. """ - def subscribe(observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: return Disposable() return Observable(subscribe) diff --git a/rx/core/observable/returnvalue.py b/rx/core/observable/returnvalue.py index 7d4649bf6..249c95db0 100644 --- a/rx/core/observable/returnvalue.py +++ b/rx/core/observable/returnvalue.py @@ -6,7 +6,9 @@ _T = TypeVar("_T") -def return_value_(value: _T, scheduler: Optional[abc.SchedulerBase] = None) -> Observable[_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'. @@ -23,7 +25,9 @@ def return_value_(value: _T, scheduler: Optional[abc.SchedulerBase] = None) -> O element. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None) -> abc.DisposableBase: + 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): @@ -35,8 +39,12 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): 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): +def _from_callable( + supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None +) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler or scheduler_ or CurrentThreadScheduler.singleton() def action(_: abc.SchedulerBase, __: Any = None): diff --git a/rx/core/observable/timer.py b/rx/core/observable/timer.py index d6906810e..0e95a5475 100644 --- a/rx/core/observable/timer.py +++ b/rx/core/observable/timer.py @@ -9,8 +9,12 @@ 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): - _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): + _scheduler: abc.SchedulerBase = ( + scheduler or scheduler_ or TimeoutScheduler.singleton() + ) def action(scheduler: abc.SchedulerBase, state: Any): observer.on_next(0) @@ -22,9 +26,13 @@ def action(scheduler: abc.SchedulerBase, state: Any): def observable_timer_duetime_and_period( - duetime: typing.AbsoluteTime, period: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + duetime: typing.AbsoluteTime, + period: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable[int]: - def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() nonlocal duetime @@ -59,7 +67,9 @@ def action(scheduler: abc.SchedulerBase, state: Any): 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): + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() d = _scheduler.to_seconds(duetime) @@ -75,12 +85,15 @@ def action(scheduler: abc.SchedulerBase, state: Any): def observable_timer_timespan_and_period( - duetime: typing.RelativeTime, period: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None + 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 + observer: abc.ObserverBase[int], + scheduler_: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index 4a659c7db..fb49f53f1 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -5,11 +5,17 @@ from rx.internal.utils import NotSet -def with_latest_from_(parent: Observable[Any], *sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: +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 subscribe_all(parent: Observable[Any], *children: Observable[Any]) -> List[SingleAssignmentDisposable]: + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + def subscribe_all( + parent: Observable[Any], *children: Observable[Any] + ) -> List[SingleAssignmentDisposable]: values = [NO_VALUE for _ in children] @@ -20,7 +26,9 @@ def on_next(value: Any) -> None: with parent.lock: values[i] = value - subscription.disposable = child.subscribe_(on_next, observer.on_error, scheduler=scheduler) + subscription.disposable = child.subscribe_( + on_next, observer.on_error, scheduler=scheduler + ) return subscription parent_subscription = SingleAssignmentDisposable() @@ -31,10 +39,14 @@ def on_next(value: Any) -> None: result = (value,) + tuple(values) observer.on_next(result) - disp = parent.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) + disp = parent.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) parent_subscription.disposable = disp - children_subscription = [subscribe_child(i, child) for i, child in enumerate(children)] + children_subscription = [ + subscribe_child(i, child) for i, child in enumerate(children) + ] return [parent_subscription] + children_subscription diff --git a/rx/core/observer/observeonobserver.py b/rx/core/observer/observeonobserver.py index 8e3189f80..41812a03f 100644 --- a/rx/core/observer/observeonobserver.py +++ b/rx/core/observer/observeonobserver.py @@ -4,7 +4,6 @@ class ObserveOnObserver(ScheduledObserver): - def _on_next_core(self, value: Any) -> None: super()._on_next_core(value) self.ensure_active() diff --git a/rx/core/observer/scheduledobserver.py b/rx/core/observer/scheduledobserver.py index e7defc9f8..657bf8549 100644 --- a/rx/core/observer/scheduledobserver.py +++ b/rx/core/observer/scheduledobserver.py @@ -10,7 +10,9 @@ class ScheduledObserver(Observer[_T_in]): - def __init__(self, scheduler: abc.SchedulerBase, observer: abc.ObserverBase[_T_in]) -> None: + def __init__( + self, scheduler: abc.SchedulerBase, observer: abc.ObserverBase[_T_in] + ) -> None: super().__init__() self.scheduler = scheduler diff --git a/rx/core/operators/bufferwithtime.py b/rx/core/operators/bufferwithtime.py index 0873d4dcd..ddd8cf0f1 100644 --- a/rx/core/operators/bufferwithtime.py +++ b/rx/core/operators/bufferwithtime.py @@ -12,4 +12,7 @@ def _buffer_with_time( if not timeshift: timeshift = timespan - return pipe(ops.window_with_time(timespan, timeshift, scheduler), ops.flat_map(lambda x: x.pipe(ops.to_iterable()))) + return pipe( + ops.window_with_time(timespan, timeshift, scheduler), + ops.flat_map(lambda x: x.pipe(ops.to_iterable())), + ) diff --git a/rx/core/operators/bufferwithtimeorcount.py b/rx/core/operators/bufferwithtimeorcount.py index bf9d977fa..b88513137 100644 --- a/rx/core/operators/bufferwithtimeorcount.py +++ b/rx/core/operators/bufferwithtimeorcount.py @@ -4,8 +4,10 @@ from rx.core import Observable, pipe -def _buffer_with_time_or_count(timespan, count, scheduler = None) -> Callable[[Observable], Observable]: +def _buffer_with_time_or_count( + timespan, count, scheduler=None +) -> Callable[[Observable], Observable]: return pipe( ops.window_with_time_or_count(timespan, count, scheduler), - ops.flat_map(lambda x: x.pipe(ops.to_iterable())) + ops.flat_map(lambda x: x.pipe(ops.to_iterable())), ) diff --git a/rx/core/operators/catch.py b/rx/core/operators/catch.py index bccdd6eaf..f1f48c46a 100644 --- a/rx/core/operators/catch.py +++ b/rx/core/operators/catch.py @@ -6,7 +6,9 @@ from rx.internal.utils import is_future -def catch_handler(source: Observable, handler: Callable[[Exception, Observable], Observable]) -> Observable: +def catch_handler( + source: Observable, handler: Callable[[Exception, Observable], Observable] +) -> Observable: def subscribe(observer, scheduler=None): d1 = SingleAssignmentDisposable() subscription = SerialDisposable() @@ -25,7 +27,9 @@ def on_error(exception): subscription.disposable = d d.disposable = result.subscribe(observer, scheduler=scheduler) - d1.disposable = source.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler) + d1.disposable = source.subscribe_( + observer.on_next, on_error, observer.on_completed, scheduler + ) return subscription return Observable(subscribe) @@ -59,6 +63,8 @@ def catch(source: Observable) -> Observable: elif isinstance(handler, abc.ObservableBase): return rx.catch(source, handler) else: - raise TypeError("catch operator takes whether an Observable or a callable handler as argument.") + raise TypeError( + "catch operator takes whether an Observable or a callable handler as argument." + ) return catch diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 1b963fa5b..4b22cd12a 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -21,9 +21,13 @@ def __init__(self, value, timestamp): def observable_delay_timespan( - source: Observable[_T], duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] = None + source: Observable[_T], + duetime: typing.RelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable[_T]: - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None + ): nonlocal duetime _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() @@ -49,7 +53,12 @@ def on_next(notification): exception[0] = notification.value.exception should_run = not running[0] else: - queue.append(Timestamp(value=notification.value, timestamp=notification.timestamp + duetime)) + queue.append( + Timestamp( + value=notification.value, + timestamp=notification.timestamp + duetime, + ) + ) should_run = not active[0] active[0] = True @@ -93,11 +102,15 @@ def action(scheduler, state): if ex: observer.on_error(ex) elif should_continue: - mad.disposable = scheduler.schedule_relative(recurse_duetime, action) + mad.disposable = scheduler.schedule_relative( + recurse_duetime, action + ) mad.disposable = _scheduler.schedule_relative(duetime, action) - subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe_(on_next, scheduler=_scheduler) + subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe_( + on_next, scheduler=_scheduler + ) return CompositeDisposable(subscription, cancelable) diff --git a/rx/core/operators/delaysubscription.py b/rx/core/operators/delaysubscription.py index d3b52d1f8..2adcc400b 100644 --- a/rx/core/operators/delaysubscription.py +++ b/rx/core/operators/delaysubscription.py @@ -6,7 +6,8 @@ def _delay_subscription( - duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + duetime: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable], Observable]: def delay_subscription(source: Observable) -> Observable: """Time shifts the observable sequence by delaying the subscription. @@ -24,6 +25,8 @@ def delay_subscription(source: Observable) -> Observable: def mapper(_) -> Observable: return rx.empty() - return source.pipe(ops.delay_with_mapper(rx.timer(duetime, scheduler=scheduler), mapper)) + return source.pipe( + ops.delay_with_mapper(rx.timer(duetime, scheduler=scheduler), mapper) + ) return delay_subscription diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index b895ba08e..d988a6c4f 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -8,7 +8,9 @@ ) -def _delay_with_mapper(subscription_delay=None, delay_duration_mapper=None) -> Callable[[Observable], Observable]: +def _delay_with_mapper( + subscription_delay=None, delay_duration_mapper=None +) -> Callable[[Observable], Observable]: def delay_with_mapper(source: Observable) -> Observable: """Time shifts the observable sequence based on a subscription delay and a delay mapper function for each element. @@ -66,19 +68,25 @@ def on_completed(): delays.remove(d) done() - d.disposable = delay.subscribe_(on_next, observer.on_error, on_completed, scheduler) + d.disposable = delay.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) def on_completed(): at_end[0] = True subscription.dispose() done() - subscription.disposable = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + subscription.disposable = source.subscribe_( + on_next, observer.on_error, on_completed, scheduler + ) if not sub_delay: start() else: - subscription.disposable = sub_delay.subscribe_(lambda _: start(), observer.on_error, start) + subscription.disposable = sub_delay.subscribe_( + lambda _: start(), observer.on_error, start + ) return CompositeDisposable(subscription, delays) diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index d0fe31454..aa08fcb38 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -24,9 +24,9 @@ def push(self, value): return ret_value -def _distinct(key_mapper: Optional[Mapper] = None, - comparer: Optional[Comparer] = None - ) -> Callable[[Observable], Observable]: +def _distinct( + key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None +) -> Callable[[Observable], Observable]: comparer = comparer or default_comparer def distinct(source: Observable) -> Observable: @@ -61,6 +61,11 @@ def on_next(x): return hashset.push(key) and observer.on_next(x) - return source.subscribe_(on_next, observer.on_error,observer.on_completed, scheduler) + + return source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler + ) + return Observable(subscribe) + return distinct diff --git a/rx/core/operators/dowhile.py b/rx/core/operators/dowhile.py index 33dc74823..1bff1ad1c 100644 --- a/rx/core/operators/dowhile.py +++ b/rx/core/operators/dowhile.py @@ -19,4 +19,5 @@ def _do_while(condition: Callable[[Any], bool]) -> Callable[[Observable], Observ def do_while(source: Observable) -> Observable: return source.pipe(ops.concat(source.pipe(ops.while_do(condition)))) + return do_while diff --git a/rx/core/operators/elementatordefault.py b/rx/core/operators/elementatordefault.py index c4a41d3e0..ede12fde7 100644 --- a/rx/core/operators/elementatordefault.py +++ b/rx/core/operators/elementatordefault.py @@ -3,7 +3,6 @@ from rx.core import Observable, abc from rx.internal.exceptions import ArgumentOutOfRangeException - _T = TypeVar("_T") diff --git a/rx/core/operators/expand.py b/rx/core/operators/expand.py index 22eda9eb7..a4ec4ada7 100644 --- a/rx/core/operators/expand.py +++ b/rx/core/operators/expand.py @@ -22,6 +22,7 @@ def expand(source: Observable) -> Observable: An observable sequence containing all the elements produced by the recursive expansion. """ + def subscribe(observer, scheduler=None): scheduler = scheduler or ImmediateScheduler.singleton() @@ -66,7 +67,9 @@ def on_complete(): if active_count[0] == 0: observer.on_completed() - sad.disposable = work.subscribe_(on_next, observer.on_error, on_complete, scheduler) + sad.disposable = work.subscribe_( + on_next, observer.on_error, on_complete, scheduler + ) m.disposable = scheduler.schedule(action) if is_owner: @@ -76,5 +79,7 @@ def on_complete(): active_count[0] += 1 ensure_active() return d + return Observable(subscribe) + return expand diff --git a/rx/core/operators/finallyaction.py b/rx/core/operators/finallyaction.py index 2e921dc4c..d4fa60817 100644 --- a/rx/core/operators/finallyaction.py +++ b/rx/core/operators/finallyaction.py @@ -1,9 +1,8 @@ from typing import Callable, Optional, TypeVar -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.disposable import Disposable - _T = TypeVar("_T") diff --git a/rx/core/operators/find.py b/rx/core/operators/find.py index 3425e9c61..bb86ed928 100644 --- a/rx/core/operators/find.py +++ b/rx/core/operators/find.py @@ -2,7 +2,6 @@ from rx.core import Observable, abc - _T = TypeVar("_T") diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index ec9655aeb..84960d881 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -7,12 +7,14 @@ from rx.subject import Subject -def _group_by(key_mapper: Mapper, - element_mapper: Optional[Mapper] = None, - subject_mapper: Optional[Callable[[], Subject]] = None, - ) -> Callable[[Observable], Observable]: - +def _group_by( + key_mapper: Mapper, + element_mapper: Optional[Mapper] = None, + subject_mapper: Optional[Callable[[], Subject]] = None, +) -> Callable[[Observable], Observable]: def duration_mapper(_): return rx.never() - return ops.group_by_until(key_mapper, element_mapper, duration_mapper, subject_mapper) + return ops.group_by_until( + key_mapper, element_mapper, duration_mapper, subject_mapper + ) diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index 76849e923..e307a506e 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -13,11 +13,12 @@ from rx.subject import Subject -def _group_by_until(key_mapper: Mapper, - element_mapper: Optional[Mapper], - duration_mapper: Callable[[GroupedObservable], Observable], - subject_mapper: Optional[Callable[[], Subject]] = None, - ) -> Callable[[Observable], Observable]: +def _group_by_until( + key_mapper: Mapper, + element_mapper: Optional[Mapper], + duration_mapper: Callable[[GroupedObservable], Observable], + subject_mapper: Optional[Callable[[], Subject]] = None, +) -> Callable[[Observable], Observable]: """Groups the elements of an observable sequence according to a specified key mapper function. A duration mapper function is used to control the lifetime of groups. When a group expires, it receives @@ -116,7 +117,9 @@ def on_error(exn): def on_completed(): expire() - sad.disposable = duration.pipe(ops.take(1)).subscribe_(on_next, on_error, on_completed, scheduler) + sad.disposable = duration.pipe(ops.take(1)).subscribe_( + on_next, on_error, on_completed, scheduler + ) try: element = element_mapper(x) @@ -141,7 +144,11 @@ def on_completed(): observer.on_completed() - group_disposable.add(source.subscribe_(on_next, on_error, on_completed, scheduler)) + group_disposable.add( + source.subscribe_(on_next, on_error, on_completed, scheduler) + ) return ref_count_disposable + return Observable(subscribe) + return group_by_until diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index 658397037..3114fe74c 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -15,10 +15,11 @@ log = logging.getLogger("Rx") -def _group_join(right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], - ) -> Callable[[Observable], Observable]: +def _group_join( + right: Observable, + left_duration_mapper: Callable[[Any], Observable], + right_duration_mapper: Callable[[Any], Observable], +) -> Callable[[Observable], Observable]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. @@ -96,7 +97,9 @@ def on_error(error): observer.on_error(error) - md.disposable = duration.pipe(ops.take(1)).subscribe_(nothing, on_error, expire, scheduler) + md.disposable = duration.pipe(ops.take(1)).subscribe_( + nothing, on_error, expire, scheduler + ) def on_error_left(error): for left_value in left_map.values(): @@ -104,7 +107,11 @@ def on_error_left(error): observer.on_error(error) - group.add(left.subscribe_(on_next_left, on_error_left, observer.on_completed, scheduler)) + group.add( + left.subscribe_( + on_next_left, on_error_left, observer.on_completed, scheduler + ) + ) def send_right(value): with left.lock: @@ -135,7 +142,9 @@ def on_error(error): observer.on_error(error) - md.disposable = duration.pipe(ops.take(1)).subscribe_(nothing, on_error, expire, scheduler) + md.disposable = duration.pipe(ops.take(1)).subscribe_( + nothing, on_error, expire, scheduler + ) with left.lock: for left_value in left_map.values(): @@ -149,5 +158,7 @@ def on_error_right(error): group.add(right.subscribe_(send_right, on_error_right, scheduler=scheduler)) return rcd + return Observable(subscribe) + return group_join diff --git a/rx/core/operators/join.py b/rx/core/operators/join.py index 772816977..e9f71c896 100644 --- a/rx/core/operators/join.py +++ b/rx/core/operators/join.py @@ -7,11 +7,11 @@ from rx.operators import take -def _join(right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], - ) -> Callable[[Observable], Observable]: - +def _join( + right: Observable, + left_duration_mapper: Callable[[Any], Observable], + right_duration_mapper: Callable[[Any], Observable], +) -> Callable[[Observable], Observable]: def join(source: Observable) -> Observable: """Correlates the elements of two sequences based on overlapping durations. @@ -60,7 +60,9 @@ def expire(): observer.on_error(exception) return - md.disposable = duration.pipe(take(1)).subscribe_(noop, observer.on_error, lambda: expire(), scheduler) + md.disposable = duration.pipe(take(1)).subscribe_( + noop, observer.on_error, lambda: expire(), scheduler + ) for val in right_map.values(): result = (value, val) @@ -72,7 +74,11 @@ def on_completed_left(): if right_done or not len(left_map): observer.on_completed() - group.add(left.subscribe_(on_next_left, observer.on_error, on_completed_left, scheduler)) + group.add( + left.subscribe_( + on_next_left, observer.on_error, on_completed_left, scheduler + ) + ) def on_next_right(value): nonlocal right_id @@ -97,7 +103,9 @@ def expire(): observer.on_error(exception) return - md.disposable = duration.pipe(take(1)).subscribe_(noop, observer.on_error, lambda: expire(), scheduler) + md.disposable = duration.pipe(take(1)).subscribe_( + noop, observer.on_error, lambda: expire(), scheduler + ) for val in left_map.values(): result = (val, value) @@ -109,7 +117,13 @@ def on_completed_right(): if left_done or not len(right_map): observer.on_completed() - group.add(right.subscribe_(on_next_right, observer.on_error, on_completed_right, scheduler)) + group.add( + right.subscribe_( + on_next_right, observer.on_error, on_completed_right, scheduler + ) + ) return group + return Observable(subscribe) + return join diff --git a/rx/core/operators/last.py b/rx/core/operators/last.py index 93b5989ec..41ed8f1fd 100644 --- a/rx/core/operators/last.py +++ b/rx/core/operators/last.py @@ -9,7 +9,9 @@ _T = TypeVar("_T") -def last_(predicate: Optional[Predicate[_T]] = None) -> Callable[[Observable[_T]], Observable[_T]]: +def last_( + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: def last(source: Observable[_T]) -> Observable[_T]: """Partially applied last operator. diff --git a/rx/core/operators/lastordefault.py b/rx/core/operators/lastordefault.py index ad9304bc6..988765c76 100644 --- a/rx/core/operators/lastordefault.py +++ b/rx/core/operators/lastordefault.py @@ -10,7 +10,9 @@ def last_or_default_async( source: Observable[_T], has_default: bool = False, default_value: Any = None ) -> Observable[_T]: - def subscribe(observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ): value = [default_value] seen_value = [False] @@ -57,4 +59,5 @@ def last_or_default(source: Observable[_T]) -> Observable[_T]: return last_or_default -__all__ = [ "last_or_default", "last_or_default_async"] \ No newline at end of file + +__all__ = ["last_or_default", "last_or_default_async"] diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index e526dc1d7..91c63027f 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -6,8 +6,11 @@ from rx.subject import BehaviorSubject -def _publish_value(initial_value: Any, mapper: Optional[Mapper] = None) -> Callable[[Observable], Observable]: +def _publish_value( + initial_value: Any, mapper: Optional[Mapper] = None +) -> Callable[[Observable], Observable]: if mapper: + def subject_factory(scheduler): return BehaviorSubject(initial_value) diff --git a/rx/core/operators/skipuntilwithtime.py b/rx/core/operators/skipuntilwithtime.py index 1370ab996..cb987fb52 100644 --- a/rx/core/operators/skipuntilwithtime.py +++ b/rx/core/operators/skipuntilwithtime.py @@ -9,7 +9,8 @@ def skip_until_with_time_( - start_time: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None + start_time: typing.AbsoluteOrRelativeTime, + scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: def skip_until_with_time(source: Observable[_T]) -> Observable[_T]: """Skips elements from the observable source sequence until the @@ -38,7 +39,10 @@ def skip_until_with_time(source: Observable[_T]) -> Observable[_T]: else: scheduler_method = "schedule_relative" - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() open = [False] @@ -47,7 +51,9 @@ def on_next(x: _T) -> None: if open[0]: observer.on_next(x) - subscription = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler_) + subscription = source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler_ + ) def action(scheduler: abc.SchedulerBase, state: Any): open[0] = True diff --git a/rx/core/operators/skipwithtime.py b/rx/core/operators/skipwithtime.py index 45a869517..3643ac3bf 100644 --- a/rx/core/operators/skipwithtime.py +++ b/rx/core/operators/skipwithtime.py @@ -37,7 +37,10 @@ def skip_with_time(source: Observable[_T]) -> Observable[_T]: specified duration from the start of the source sequence. """ - def subscribe(observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler_: Optional[abc.SchedulerBase] = None, + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() open = [False] @@ -50,7 +53,9 @@ def on_next(x: _T): if open[0]: observer.on_next(x) - d = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler_) + d = source.subscribe_( + on_next, observer.on_error, observer.on_completed, scheduler_ + ) return CompositeDisposable(t, d) return Observable(subscribe) diff --git a/rx/core/operators/statistics.py b/rx/core/operators/statistics.py index 2ef2f017f..8b733c1f2 100644 --- a/rx/core/operators/statistics.py +++ b/rx/core/operators/statistics.py @@ -27,11 +27,13 @@ def mode(source: Observable) -> Observable: Returns the most frequently emitted value (or "values" if they have the same number of occurrences). The sequence must be finite. """ - return source.group_by(lambda v: v) \ - .flat_map(lambda grp: grp.count().map(lambda ct: (grp.key, ct))) \ - .to_sorted_list(lambda t: t[1], reverse=True) \ - .flat_map(lambda l: Observable.from_(l).take_while(lambda t: t[1] == l[0][1])) \ + return ( + source.group_by(lambda v: v) + .flat_map(lambda grp: grp.count().map(lambda ct: (grp.key, ct))) + .to_sorted_list(lambda t: t[1], reverse=True) + .flat_map(lambda l: Observable.from_(l).take_while(lambda t: t[1] == l[0][1])) .map(lambda t: t[0]) + ) def variance(source: Observable) -> Observable: @@ -39,13 +41,21 @@ def variance(source: Observable) -> Observable: Returns the statistical variance of the numerical emissions. The sequence must be finite. """ - squared_values = source.to_list() \ - .flat_map(lambda l: Observable.from_(l).average().flat_map(lambda avg: Observable.from_(l).map(lambda i: i - avg))) \ - .map(lambda i: i * i) \ - .publish() \ + squared_values = ( + source.to_list() + .flat_map( + lambda l: Observable.from_(l) + .average() + .flat_map(lambda avg: Observable.from_(l).map(lambda i: i - avg)) + ) + .map(lambda i: i * i) + .publish() .auto_connect(2) + ) - return Observable.zip(squared_values.sum(), squared_values.count(), lambda sum, ct: sum / (ct - 1)) + return Observable.zip( + squared_values.sum(), squared_values.count(), lambda sum, ct: sum / (ct - 1) + ) def standard_deviation(source: Observable) -> Observable: @@ -54,5 +64,3 @@ def standard_deviation(source: Observable) -> Observable: The sequence must be finite. """ return source.variance().map(lambda i: math.sqrt(i)) - - diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index 11be0d69f..ecadfe177 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from datetime import datetime -from typing import Any, Callable, Optional, TypeVar, Generic +from typing import Any, Callable, Generic, Optional, TypeVar from rx import defer, operators from rx.core import Observable, abc diff --git a/rx/disposable/refcountdisposable.py b/rx/disposable/refcountdisposable.py index 5a028c0ec..b843f1e6e 100644 --- a/rx/disposable/refcountdisposable.py +++ b/rx/disposable/refcountdisposable.py @@ -2,6 +2,7 @@ from typing import Optional from rx.core.abc import DisposableBase + from .disposable import Disposable diff --git a/rx/internal/exceptions.py b/rx/internal/exceptions.py index bdc1a62da..0a36a251a 100644 --- a/rx/internal/exceptions.py +++ b/rx/internal/exceptions.py @@ -6,12 +6,16 @@ class SequenceContainsNoElementsError(Exception): def __init__(self, msg: Optional[str] = None): - super(SequenceContainsNoElementsError, self).__init__(msg or "Sequence contains no elements") + super(SequenceContainsNoElementsError, self).__init__( + msg or "Sequence contains no elements" + ) class ArgumentOutOfRangeException(ValueError): def __init__(self, msg: Optional[str] = None): - super(ArgumentOutOfRangeException, self).__init__(msg or "Argument out of range") + super(ArgumentOutOfRangeException, self).__init__( + msg or "Argument out of range" + ) class DisposedException(Exception): diff --git a/rx/scheduler/catchscheduler.py b/rx/scheduler/catchscheduler.py index 5eeccb1f3..fa0a389a3 100644 --- a/rx/scheduler/catchscheduler.py +++ b/rx/scheduler/catchscheduler.py @@ -11,7 +11,9 @@ class CatchScheduler(PeriodicScheduler): - def __init__(self, scheduler: abc.SchedulerBase, handler: Callable[[Exception], bool]) -> None: + def __init__( + self, scheduler: abc.SchedulerBase, handler: Callable[[Exception], bool] + ) -> None: """Wraps a scheduler, passed as constructor argument, adding exception handling for scheduled actions. The handler should return True to indicate it handled the exception successfully. Falsy return values will @@ -41,7 +43,9 @@ def now(self) -> datetime: return self._scheduler.now - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -57,7 +61,10 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt return self._scheduler.schedule(action, state=state) def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -75,7 +82,10 @@ def schedule_relative( return self._scheduler.schedule_relative(duetime, action, state=state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. @@ -139,10 +149,14 @@ def periodic(state: Optional[_TState] = None) -> Optional[_TState]: def _clone(self, scheduler: abc.SchedulerBase) -> "CatchScheduler": return CatchScheduler(scheduler, self._handler) - def _wrap(self, action: typing.ScheduledAction[_TState]) -> typing.ScheduledAction[_TState]: + def _wrap( + self, action: typing.ScheduledAction[_TState] + ) -> typing.ScheduledAction[_TState]: parent = self - def wrapped_action(self: CatchScheduler, state: Optional[_TState]) -> Optional[abc.DisposableBase]: + def wrapped_action( + self: CatchScheduler, state: Optional[_TState] + ) -> Optional[abc.DisposableBase]: try: return action(parent._get_recursive_wrapper(self), state) except Exception as ex: diff --git a/rx/scheduler/currentthreadscheduler.py b/rx/scheduler/currentthreadscheduler.py index ee7defd0e..94cdf2c50 100644 --- a/rx/scheduler/currentthreadscheduler.py +++ b/rx/scheduler/currentthreadscheduler.py @@ -6,7 +6,7 @@ from .trampoline import Trampoline from .trampolinescheduler import TrampolineScheduler -log = logging.getLogger('Rx') +log = logging.getLogger("Rx") class CurrentThreadScheduler(TrampolineScheduler): @@ -21,12 +21,11 @@ class CurrentThreadScheduler(TrampolineScheduler): """ _global: MutableMapping[ - type, - MutableMapping[Thread, 'CurrentThreadScheduler'] + type, MutableMapping[Thread, "CurrentThreadScheduler"] ] = WeakKeyDictionary() @classmethod - def singleton(cls) -> 'CurrentThreadScheduler': + def singleton(cls) -> "CurrentThreadScheduler": """ Obtain a singleton instance for the current thread. Please note, if you pass this instance to another thread, it will effectively behave as @@ -61,7 +60,6 @@ def get_trampoline(self) -> Trampoline: class _Local(local): - def __init__(self) -> None: super().__init__() self.tramp = Trampoline() diff --git a/rx/scheduler/eventloop/asyncioscheduler.py b/rx/scheduler/eventloop/asyncioscheduler.py index da4fe2d74..857552590 100644 --- a/rx/scheduler/eventloop/asyncioscheduler.py +++ b/rx/scheduler/eventloop/asyncioscheduler.py @@ -27,7 +27,9 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: super().__init__() self._loop: asyncio.AbstractEventLoop = loop - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/eventloop/eventletscheduler.py b/rx/scheduler/eventloop/eventletscheduler.py index e06a49bde..4ead50e77 100644 --- a/rx/scheduler/eventloop/eventletscheduler.py +++ b/rx/scheduler/eventloop/eventletscheduler.py @@ -28,7 +28,9 @@ def __init__(self, eventlet: Any) -> None: super().__init__() self._eventlet = eventlet - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/eventloop/ioloopscheduler.py b/rx/scheduler/eventloop/ioloopscheduler.py index 46c801998..b2655ea60 100644 --- a/rx/scheduler/eventloop/ioloopscheduler.py +++ b/rx/scheduler/eventloop/ioloopscheduler.py @@ -29,7 +29,9 @@ def __init__(self, loop: Any) -> None: super().__init__() self._loop = loop - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/eventloop/twistedscheduler.py b/rx/scheduler/eventloop/twistedscheduler.py index 2ff73717e..88dd1924f 100644 --- a/rx/scheduler/eventloop/twistedscheduler.py +++ b/rx/scheduler/eventloop/twistedscheduler.py @@ -25,7 +25,9 @@ def __init__(self, reactor: Any) -> None: super().__init__() self._reactor = reactor - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/eventloopscheduler.py b/rx/scheduler/eventloopscheduler.py index 9cd386839..133ba157a 100644 --- a/rx/scheduler/eventloopscheduler.py +++ b/rx/scheduler/eventloopscheduler.py @@ -21,11 +21,17 @@ class EventLoopScheduler(PeriodicScheduler, abc.DisposableBase): """Creates an object that schedules units of work on a designated thread.""" - def __init__(self, thread_factory: Optional[typing.StartableFactory] = None, exit_if_empty: bool = False) -> None: + def __init__( + self, + thread_factory: Optional[typing.StartableFactory] = None, + exit_if_empty: bool = False, + ) -> None: super().__init__() self._is_disposed = False - self._thread_factory: typing.StartableFactory = thread_factory or default_thread_factory + self._thread_factory: typing.StartableFactory = ( + thread_factory or default_thread_factory + ) self._thread: Optional[typing.Startable] = None self._condition = threading.Condition(threading.Lock()) self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() @@ -33,7 +39,9 @@ def __init__(self, thread_factory: Optional[typing.StartableFactory] = None, exi self._exit_if_empty = exit_if_empty - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -48,7 +56,10 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt return self.schedule_absolute(self.now, action, state=state) def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -66,7 +77,10 @@ def schedule_relative( return self.schedule_absolute(self.now + duetime, action, state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/historicalscheduler.py b/rx/scheduler/historicalscheduler.py index 594c2a004..3e54b9a6a 100644 --- a/rx/scheduler/historicalscheduler.py +++ b/rx/scheduler/historicalscheduler.py @@ -33,10 +33,9 @@ def now(self) -> datetime: return self._clock @classmethod - def add(cls, - absolute: typing.AbsoluteTime, - relative: typing.RelativeTime - ) -> typing.AbsoluteTime: + def add( + cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime + ) -> typing.AbsoluteTime: """Adds a relative time value to an absolute time value. Args: diff --git a/rx/scheduler/immediatescheduler.py b/rx/scheduler/immediatescheduler.py index 11f62582b..54862ca39 100644 --- a/rx/scheduler/immediatescheduler.py +++ b/rx/scheduler/immediatescheduler.py @@ -34,7 +34,9 @@ def singleton(cls) -> "ImmediateScheduler": def __new__(cls) -> "ImmediateScheduler": return cls.singleton() - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -49,7 +51,10 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt return self.invoke_action(action, state) def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -70,7 +75,10 @@ def schedule_relative( return self.invoke_action(action, state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/mainloop/gtkscheduler.py b/rx/scheduler/mainloop/gtkscheduler.py index 88ce19712..1928230f2 100644 --- a/rx/scheduler/mainloop/gtkscheduler.py +++ b/rx/scheduler/mainloop/gtkscheduler.py @@ -48,7 +48,9 @@ def timer_handler(_) -> bool: if periodic: state = cast(typing.ScheduledPeriodicAction, action)(state) else: - sad.disposable = self.invoke_action(cast(typing.ScheduledAction[_TState], action), state=state) + sad.disposable = self.invoke_action( + cast(typing.ScheduledAction[_TState], action), state=state + ) return periodic @@ -60,7 +62,9 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/mainloop/pygamescheduler.py b/rx/scheduler/mainloop/pygamescheduler.py index 81963f3d9..58215fa64 100644 --- a/rx/scheduler/mainloop/pygamescheduler.py +++ b/rx/scheduler/mainloop/pygamescheduler.py @@ -35,7 +35,9 @@ def __init__(self, pygame: Any): self._lock = threading.Lock() self._queue: PriorityQueue[ScheduledItem] = PriorityQueue() - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/mainloop/qtscheduler.py b/rx/scheduler/mainloop/qtscheduler.py index 70da7e246..31bd5062b 100644 --- a/rx/scheduler/mainloop/qtscheduler.py +++ b/rx/scheduler/mainloop/qtscheduler.py @@ -27,7 +27,9 @@ def __init__(self, qtcore: Any): timer_class: Any = self._qtcore.QTimer self._periodic_timers: Set[timer_class] = set() - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/mainloop/tkinterscheduler.py b/rx/scheduler/mainloop/tkinterscheduler.py index bf38ba130..6ac42b020 100644 --- a/rx/scheduler/mainloop/tkinterscheduler.py +++ b/rx/scheduler/mainloop/tkinterscheduler.py @@ -25,7 +25,9 @@ def __init__(self, root: Any) -> None: super().__init__() self._root = root - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/mainloop/wxscheduler.py b/rx/scheduler/mainloop/wxscheduler.py index aaf92fa6b..6e1e275f0 100644 --- a/rx/scheduler/mainloop/wxscheduler.py +++ b/rx/scheduler/mainloop/wxscheduler.py @@ -62,14 +62,18 @@ def interval() -> None: if periodic: state = cast(typing.ScheduledPeriodicAction, action)(state) else: - sad.disposable = cast(typing.ScheduledAction[_TState], action)(scheduler, state) + sad.disposable = cast(typing.ScheduledAction[_TState], action)( + scheduler, state + ) msecs = max(1, int(self.to_seconds(time) * 1000.0)) # Must be non-zero log.debug("timeout wx: %s", msecs) timer = self._timer_class(interval) - timer.Start(msecs, self._wx.TIMER_CONTINUOUS if periodic else self._wx.TIMER_ONE_SHOT) + timer.Start( + msecs, self._wx.TIMER_CONTINUOUS if periodic else self._wx.TIMER_ONE_SHOT + ) self._timers.add(timer) def dispose() -> None: @@ -78,7 +82,9 @@ def dispose() -> None: return CompositeDisposable(sad, Disposable(dispose)) - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: diff --git a/rx/scheduler/newthreadscheduler.py b/rx/scheduler/newthreadscheduler.py index ea9f11b92..093e9600b 100644 --- a/rx/scheduler/newthreadscheduler.py +++ b/rx/scheduler/newthreadscheduler.py @@ -18,11 +18,17 @@ class NewThreadScheduler(PeriodicScheduler): """Creates an object that schedules each unit of work on a separate thread.""" - def __init__(self, thread_factory: Optional[typing.StartableFactory] = None) -> None: + def __init__( + self, thread_factory: Optional[typing.StartableFactory] = None + ) -> None: super().__init__() - self.thread_factory: typing.StartableFactory = thread_factory or default_thread_factory + self.thread_factory: typing.StartableFactory = ( + thread_factory or default_thread_factory + ) - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -34,11 +40,16 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt (best effort). """ - scheduler = EventLoopScheduler(thread_factory=self.thread_factory, exit_if_empty=True) + scheduler = EventLoopScheduler( + thread_factory=self.thread_factory, exit_if_empty=True + ) return scheduler.schedule(action, state) def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -52,11 +63,16 @@ def schedule_relative( (best effort). """ - scheduler = EventLoopScheduler(thread_factory=self.thread_factory, exit_if_empty=True) + scheduler = EventLoopScheduler( + thread_factory=self.thread_factory, exit_if_empty=True + ) return scheduler.schedule_relative(duetime, action, state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/periodicscheduler.py b/rx/scheduler/periodicscheduler.py index 12cc697c0..6069a0eb4 100644 --- a/rx/scheduler/periodicscheduler.py +++ b/rx/scheduler/periodicscheduler.py @@ -38,7 +38,9 @@ def schedule_periodic( disp: MultipleAssignmentDisposable = MultipleAssignmentDisposable() seconds: float = self.to_seconds(period) - def periodic(scheduler: abc.SchedulerBase, state: Optional[_TState] = None) -> Optional[Disposable]: + def periodic( + scheduler: abc.SchedulerBase, state: Optional[_TState] = None + ) -> Optional[Disposable]: if disp.is_disposed: return None @@ -59,7 +61,9 @@ def periodic(scheduler: abc.SchedulerBase, state: Optional[_TState] = None) -> O return disp @abstractmethod - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -75,7 +79,10 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt @abstractmethod def schedule_relative( - self, duetime: typing.RelativeTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -93,7 +100,10 @@ def schedule_relative( @abstractmethod def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/scheduleditem.py b/rx/scheduler/scheduleditem.py index d177c3f94..1c1f355a7 100644 --- a/rx/scheduler/scheduleditem.py +++ b/rx/scheduler/scheduleditem.py @@ -11,7 +11,11 @@ class ScheduledItem(object): def __init__( - self, scheduler: Scheduler, state: Optional[_TState], action: abc.ScheduledAction[_TState], duetime: datetime + self, + scheduler: Scheduler, + state: Optional[_TState], + action: abc.ScheduledAction[_TState], + duetime: datetime, ) -> None: self.scheduler: Scheduler = scheduler self.state: Optional[Any] = state diff --git a/rx/scheduler/scheduler.py b/rx/scheduler/scheduler.py index c5fac1188..8786271cb 100644 --- a/rx/scheduler/scheduler.py +++ b/rx/scheduler/scheduler.py @@ -29,7 +29,9 @@ def now(self) -> datetime: return default_now() @abstractmethod - def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -45,7 +47,10 @@ def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState @abstractmethod def schedule_relative( - self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -63,7 +68,10 @@ def schedule_relative( @abstractmethod def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/threadpoolscheduler.py b/rx/scheduler/threadpoolscheduler.py index b41586ce1..a9b2e4375 100644 --- a/rx/scheduler/threadpoolscheduler.py +++ b/rx/scheduler/threadpoolscheduler.py @@ -12,7 +12,9 @@ class ThreadPoolScheduler(NewThreadScheduler): class ThreadPoolThread(abc.StartableBase): """Wraps a concurrent future as a thread.""" - def __init__(self, executor: ThreadPoolExecutor, target: typing.StartableTarget): + def __init__( + self, executor: ThreadPoolExecutor, target: typing.StartableTarget + ): self.executor: ThreadPoolExecutor = executor self.target: typing.StartableTarget = target self.future: Optional[Future] = None diff --git a/rx/scheduler/trampolinescheduler.py b/rx/scheduler/trampolinescheduler.py index b86a83789..6e9beeb16 100644 --- a/rx/scheduler/trampolinescheduler.py +++ b/rx/scheduler/trampolinescheduler.py @@ -31,7 +31,9 @@ def __init__(self) -> None: def get_trampoline(self) -> Trampoline: return self._tramp - def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -46,7 +48,10 @@ def schedule(self, action: abc.ScheduledAction[_TState], state: Optional[_TState return self.schedule_absolute(self.now, action, state=state) def schedule_relative( - self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -64,7 +69,10 @@ def schedule_relative( return self.schedule_absolute(self.now + duetime, action, state=state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index 1d3a1f6c5..dbdf3624f 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -53,7 +53,9 @@ def now(self) -> datetime: return self.to_datetime(self._clock) - def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None) -> abc.DisposableBase: + def schedule( + self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None + ) -> abc.DisposableBase: """Schedules an action to be executed. Args: @@ -68,7 +70,10 @@ def schedule(self, action: typing.ScheduledAction[_TState], state: Optional[_TSt return self.schedule_absolute(self._clock, action, state=state) def schedule_relative( - self, duetime: typing.RelativeTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.RelativeTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed after duetime. @@ -86,7 +91,10 @@ def schedule_relative( return self.schedule_absolute(time, action, state=state) def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: abc.ScheduledAction[_TState], state: Optional[_TState] = None + self, + duetime: typing.AbsoluteTime, + action: abc.ScheduledAction[_TState], + state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules an action to be executed at duetime. @@ -225,7 +233,9 @@ def sleep(self, time: typing.RelativeTime) -> None: @classmethod @abstractmethod - def add(cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime) -> typing.AbsoluteTime: + def add( + cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime + ) -> typing.AbsoluteTime: """Adds a relative time value to an absolute time value. Args: diff --git a/rx/subject/behaviorsubject.py b/rx/subject/behaviorsubject.py index 39f0e8dde..ad66c5397 100644 --- a/rx/subject/behaviorsubject.py +++ b/rx/subject/behaviorsubject.py @@ -1,7 +1,8 @@ from typing import Any, Optional, TypeVar -from rx.disposable import Disposable from rx.core import abc +from rx.disposable import Disposable + from .innersubscription import InnerSubscription from .subject import Subject diff --git a/rx/subject/subject.py b/rx/subject/subject.py index de4bc9b61..d70efeb80 100644 --- a/rx/subject/subject.py +++ b/rx/subject/subject.py @@ -30,7 +30,9 @@ def check_disposed(self) -> None: raise DisposedException() def _subscribe_core( - self, observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: with self.lock: self.check_disposed() diff --git a/rx/testing/coldobservable.py b/rx/testing/coldobservable.py index 020a7f833..4e496650d 100644 --- a/rx/testing/coldobservable.py +++ b/rx/testing/coldobservable.py @@ -1,6 +1,6 @@ from typing import Any, List, Optional, TypeVar -from rx.core import Observable, abc, Notification +from rx.core import Notification, Observable, abc from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import VirtualTimeScheduler diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index dd2ab616b..4abec988d 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -13,7 +13,7 @@ new_thread_scheduler = NewThreadScheduler() -MarblesContext = namedtuple('MarblesContext', 'start, cold, hot, exp') +MarblesContext = namedtuple("MarblesContext", "start, cold, hot, exp") @contextmanager @@ -63,20 +63,22 @@ def marbles_testing(timespan=1.0): def check(): if outside_of_context: - warn('context functions should not be called outside of ' - 'with statement.', - UserWarning, - stacklevel=3, - ) + warn( + "context functions should not be called outside of " "with statement.", + UserWarning, + stacklevel=3, + ) if start_called: - warn('start() should only be called one time inside ' - 'a with statement.', - UserWarning, - stacklevel=3, - ) + warn( + "start() should only be called one time inside " "a with statement.", + UserWarning, + stacklevel=3, + ) - def test_start(create: Union[Observable, Callable[[], Observable]]) -> List[Recorded]: + def test_start( + create: Union[Observable, Callable[[], Observable]] + ) -> List[Recorded]: nonlocal start_called check() @@ -93,30 +95,36 @@ def default_create(): created=created, subscribed=subscribed, disposed=disposed, - ) + ) start_called = True return mock_observer.messages - def test_expected(string: str, lookup: Dict = None, error: Exception = None) -> List[Recorded]: + def test_expected( + string: str, lookup: Dict = None, error: Exception = None + ) -> List[Recorded]: messages = parse( string, timespan=timespan, time_shift=subscribed, lookup=lookup, error=error, - ) + ) return messages_to_records(messages) - def test_cold(string: str, lookup: Dict = None, error: Exception = None) -> Observable: + def test_cold( + string: str, lookup: Dict = None, error: Exception = None + ) -> Observable: check() return rx.from_marbles( string, timespan=timespan, lookup=lookup, error=error, - ) + ) - def test_hot(string: str, lookup: Dict = None, error: Exception = None) -> Observable: + def test_hot( + string: str, lookup: Dict = None, error: Exception = None + ) -> Observable: check() hot_obs = rx.hot( string, @@ -125,7 +133,7 @@ def test_hot(string: str, lookup: Dict = None, error: Exception = None) -> Obser lookup=lookup, error=error, scheduler=scheduler, - ) + ) return hot_obs try: @@ -134,8 +142,9 @@ def test_hot(string: str, lookup: Dict = None, error: Exception = None) -> Obser outside_of_context = True -def messages_to_records(messages: List[Tuple[RelativeTime, Notification]] - ) -> List[Recorded]: +def messages_to_records( + messages: List[Tuple[RelativeTime, Notification]] +) -> List[Recorded]: """ Helper function to convert messages returned by parse() to a list of Recorded. @@ -145,8 +154,8 @@ def messages_to_records(messages: List[Tuple[RelativeTime, Notification]] dispatcher = dict( N=lambda t, n: ReactiveTest.on_next(t, n.value), E=lambda t, n: ReactiveTest.on_error(t, n.exception), - C=lambda t, n: ReactiveTest.on_completed(t) - ) + C=lambda t, n: ReactiveTest.on_completed(t), + ) for message in messages: time, notification = message diff --git a/rx/testing/mockdisposable.py b/rx/testing/mockdisposable.py index ff48c31ef..fce202676 100644 --- a/rx/testing/mockdisposable.py +++ b/rx/testing/mockdisposable.py @@ -1,6 +1,6 @@ from typing import List -from rx.core import typing, abc +from rx.core import abc, typing from rx.scheduler import VirtualTimeScheduler diff --git a/rx/testing/reactivetest.py b/rx/testing/reactivetest.py index dd8e2bec1..5938567b2 100644 --- a/rx/testing/reactivetest.py +++ b/rx/testing/reactivetest.py @@ -15,7 +15,7 @@ def is_prime(i: int) -> bool: return False _max = int(math.floor(math.sqrt(i))) - for j in range(2, _max+1): + for j in range(2, _max + 1): if not i % j: return False @@ -32,7 +32,7 @@ def __eq__(self, other): return True if other is None: return False - if other.kind != 'N': + if other.kind != "N": return False return self.predicate(other.value) @@ -46,7 +46,7 @@ def __eq__(self, other): return True if other is None: return False - if other.kind != 'E': + if other.kind != "E": return False return self.predicate(other.exception) diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index f3b9af6c8..05309c333 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,6 +1,6 @@ -from typing import Any, TypeVar, Generic, Optional, cast +from typing import Any, Generic, Optional, TypeVar, cast -from rx.core import typing, Notification +from rx.core import Notification, typing from rx.internal.basic import default_comparer _T = TypeVar("_T") diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index 793e6bc59..51be0fb01 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -21,7 +21,10 @@ class TestScheduler(VirtualTimeScheduler): __test__ = False def schedule_absolute( - self, duetime: typing.AbsoluteTime, action: typing.ScheduledAction[_TState], state: _TState = None + self, + duetime: typing.AbsoluteTime, + action: typing.ScheduledAction[_TState], + state: _TState = None, ) -> abc.DisposableBase: """Schedules an action to be executed at the specified virtual time. diff --git a/setup.cfg b/setup.cfg index ba013ab6f..6c34a36c9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,7 +9,7 @@ test = pytest testpaths = tests [flake8] -max-line-length = 120 +max-line-length = 88 [isort] profile = black \ No newline at end of file From cc3af22ab1bc42f12f52b7d6deae8f7dc82915fe Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 14:22:47 +0100 Subject: [PATCH 024/103] Better type hints --- rx/core/observable/groupedobservable.py | 29 ++- rx/core/operators/buffer.py | 36 ++- rx/core/operators/groupby.py | 20 +- rx/core/operators/groupbyuntil.py | 54 ++-- rx/operators/__init__.py | 34 +-- rx/testing/reactivetest.py | 23 +- rx/testing/testscheduler.py | 30 ++- tests/test_observable/test_filter.py | 323 ++++++++++++++++++------ tests/test_observable/test_slice.py | 64 +++-- tests/test_scheduler/test_scheduler.py | 2 - 10 files changed, 439 insertions(+), 176 deletions(-) diff --git a/rx/core/observable/groupedobservable.py b/rx/core/observable/groupedobservable.py index c2679faac..5b85e97c4 100644 --- a/rx/core/observable/groupedobservable.py +++ b/rx/core/observable/groupedobservable.py @@ -1,16 +1,29 @@ -from rx.disposable import CompositeDisposable +from typing import TypeVar, Optional, Generic +from rx.disposable import CompositeDisposable, RefCountDisposable, Disposable +from rx.core import abc from .observable import Observable +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") -class GroupedObservable(Observable): - def __init__(self, key, underlying_observable, merged_disposable=None): + +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, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: return CompositeDisposable( - merged_disposable.disposable, + merged_disposable.disposable if merged_disposable else Disposable(), underlying_observable.subscribe(observer, scheduler=scheduler), ) @@ -18,5 +31,9 @@ def subscribe(observer, scheduler=None): underlying_observable if not merged_disposable else Observable(subscribe) ) - def _subscribe_core(self, observer, scheduler=None): + def _subscribe_core( + self, + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: return self.underlying_observable.subscribe(observer, scheduler=scheduler) diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index ebe0ca966..aff69f33d 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -10,25 +10,41 @@ def buffer_( boundaries: Observable[Any], ) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( - ops.window(boundaries), ops.flat_map(pipe(ops.to_iterable(), ops.map(list))) + ops.window(boundaries), + ops.flat_map( + pipe( + ops.to_iterable(), + ops.map(list), + ), + ), ) -def _buffer_when( - closing_mapper: Callable[[], Observable] -) -> Callable[[Observable], Observable]: +def buffer_when_( + closing_mapper: Callable[[], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window_when(closing_mapper), - ops.flat_map(pipe(ops.to_iterable(), ops.map(list))), + ops.flat_map( + pipe( + ops.to_iterable(), + ops.map(list), + ) + ), ) -def _buffer_toggle( - openings: Observable, closing_mapper: Callable[[Any], Observable] -) -> Callable[[Observable], Observable]: +def buffer_toggle_( + openings: Observable[Any], closing_mapper: Callable[[Any], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window_toggle(openings, closing_mapper), - ops.flat_map(pipe(ops.to_iterable(), ops.map(list))), + ops.flat_map( + pipe( + ops.to_iterable(), + ops.map(list), + ) + ), ) @@ -74,4 +90,4 @@ def predicate(value: List[_T]) -> bool: return buffer_with_count -__all__ = ["buffer_", "buffer_with_count_"] +__all__ = ["buffer_", "buffer_with_count_", "buffer_when_", "buffer_toggle_"] diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index 84960d881..50328edb3 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -1,20 +1,28 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, Any import rx from rx import operators as ops from rx.core import Observable +from rx.core.observable.groupedobservable import GroupedObservable from rx.core.typing import Mapper from rx.subject import Subject +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") +_TValue = TypeVar("_TValue") + def _group_by( - key_mapper: Mapper, - element_mapper: Optional[Mapper] = None, - subject_mapper: Optional[Callable[[], Subject]] = None, -) -> Callable[[Observable], Observable]: - def duration_mapper(_): + key_mapper: Mapper[_T, _TKey], + element_mapper: Optional[Mapper[_T, _TValue]] = None, + subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, +) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: + def duration_mapper(_: GroupedObservable[Any, Any]) -> Observable[Any]: return rx.never() return ops.group_by_until( key_mapper, element_mapper, duration_mapper, subject_mapper ) + + +__all__ = ["_group_by"] diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index e307a506e..a3fe4b5db 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -1,8 +1,8 @@ from collections import OrderedDict -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, Any, Type from rx import operators as ops -from rx.core import GroupedObservable, Observable +from rx.core import GroupedObservable, Observable, abc from rx.core.typing import Mapper from rx.disposable import ( CompositeDisposable, @@ -12,13 +12,17 @@ from rx.internal.basic import identity from rx.subject import Subject +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") +_TValue = TypeVar("_TValue") -def _group_by_until( - key_mapper: Mapper, - element_mapper: Optional[Mapper], - duration_mapper: Callable[[GroupedObservable], Observable], - subject_mapper: Optional[Callable[[], Subject]] = None, -) -> Callable[[Observable], Observable]: + +def group_by_until_( + key_mapper: Mapper[_T, _TKey], + element_mapper: Optional[Mapper[_T, _TValue]], + duration_mapper: Callable[[GroupedObservable[_TKey, _T]], Observable[Any]], + subject_mapper: Optional[Callable[[], Subject[_T]]] = None, +) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: """Groups the elements of an observable sequence according to a specified key mapper function. A duration mapper function is used to control the lifetime of groups. When a group expires, it receives @@ -46,16 +50,21 @@ def _group_by_until( element_mapper = element_mapper or identity - default_subject_mapper = Subject + default_subject_mapper: Callable[[], Subject[_T]] = lambda: Subject() subject_mapper = subject_mapper or default_subject_mapper - def group_by_until(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): - writers = OrderedDict() + def group_by_until( + source: Observable[_T], + ) -> Observable[GroupedObservable[_TKey, _TValue]]: + def subscribe( + observer: abc.ObserverBase[GroupedObservable[_TKey, _TValue]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: + writers: OrderedDict[_TKey, abc.ObserverBase[_TValue]] = OrderedDict() group_disposable = CompositeDisposable() ref_count_disposable = RefCountDisposable(group_disposable) - def on_next(x): + def on_next(x: _T) -> None: writer = None key = None @@ -84,8 +93,12 @@ def on_next(x): fire_new_map_entry = True if fire_new_map_entry: - group = GroupedObservable(key, writer, ref_count_disposable) - duration_group = GroupedObservable(key, writer) + group: GroupedObservable[_TKey, _TValue] = GroupedObservable( + key, writer, ref_count_disposable + ) + duration_group: GroupedObservable[_TKey, Any] = GroupedObservable( + key, writer + ) try: duration = duration_mapper(duration_group) except Exception as e: @@ -106,10 +119,10 @@ def expire(): group_disposable.remove(sad) - def on_next(value): + def on_next(value: Any) -> None: pass - def on_error(exn): + def on_error(exn: Exception) -> None: for wrt in writers.values(): wrt.on_error(exn) observer.on_error(exn) @@ -132,13 +145,13 @@ def on_completed(): writer.on_next(element) - def on_error(ex): + def on_error(ex: Exception) -> None: for wrt in writers.values(): wrt.on_error(ex) observer.on_error(ex) - def on_completed(): + def on_completed() -> None: for wrt in writers.values(): wrt.on_completed() @@ -152,3 +165,6 @@ def on_completed(): return Observable(subscribe) return group_by_until + + +__all__ = ["group_by_until_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 633b19039..4bca3bea2 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -176,8 +176,8 @@ def buffer( def buffer_when( - closing_mapper: Callable[[], Observable] -) -> Callable[[Observable], Observable]: + closing_mapper: Callable[[], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers. @@ -204,9 +204,9 @@ def buffer_when( A function that takes an observable source and returns an observable sequence of windows. """ - from rx.core.operators.buffer import _buffer_when + from rx.core.operators.buffer import buffer_when_ - return _buffer_when(closing_mapper) + return buffer_when_(closing_mapper) def buffer_toggle( @@ -240,9 +240,9 @@ def buffer_toggle( A function that takes an observable source and returns an observable sequence of windows. """ - from rx.core.operators.buffer import _buffer_toggle + from rx.core.operators.buffer import buffer_toggle_ - return _buffer_toggle(openings, closing_mapper) + return buffer_toggle_(openings, closing_mapper) def buffer_with_count( @@ -1328,10 +1328,10 @@ def fork_join(*others: Observable) -> Callable[[Observable], Observable]: def group_by( - key_mapper: Mapper, - element_mapper: Optional[Mapper] = None, - subject_mapper: Optional[Callable[[], Subject]] = None, -) -> Callable[[Observable], Observable]: + key_mapper: Mapper[_T, _TKey], + element_mapper: Optional[Mapper[_T, _TValue]] = None, + subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, +) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: """Groups the elements of an observable sequence according to a specified key mapper function and comparer and selects the resulting elements by using a specified function. @@ -1369,11 +1369,11 @@ def group_by( def group_by_until( - key_mapper: Mapper, - element_mapper: Optional[Mapper], - duration_mapper: Callable[[GroupedObservable], Observable], - subject_mapper: Optional[Callable[[], Subject]] = None, -) -> Callable[[Observable], Observable]: + key_mapper: Mapper[_T, _TKey], + element_mapper: Optional[Mapper[_T, _TValue]], + duration_mapper: Callable[[GroupedObservable[_TKey, _TValue]], Observable[Any]], + subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, +) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: """Groups the elements of an observable sequence according to a specified key mapper function. A duration mapper function is used to control the lifetime of groups. When a group expires, it @@ -1411,9 +1411,9 @@ def group_by_until( group with the same key value can be created once an element with such a key value is encountered. """ - from rx.core.operators.groupbyuntil import _group_by_until + from rx.core.operators.groupbyuntil import group_by_until_ - return _group_by_until(key_mapper, element_mapper, duration_mapper, subject_mapper) + return group_by_until_(key_mapper, element_mapper, duration_mapper, subject_mapper) def group_join( diff --git a/rx/testing/reactivetest.py b/rx/testing/reactivetest.py index 5938567b2..17eac67f1 100644 --- a/rx/testing/reactivetest.py +++ b/rx/testing/reactivetest.py @@ -1,12 +1,15 @@ import math import types -from typing import Any +from typing import Any, Generic, TypeVar, Union from rx.core.notification import OnCompleted, OnError, OnNext +from rx.core import typing from .recorded import Recorded from .subscription import Subscription +_T = TypeVar("_T") + def is_prime(i: int) -> bool: """Tests if number is prime or not""" @@ -23,11 +26,11 @@ def is_prime(i: int) -> bool: # New predicate tests -class OnNextPredicate: - def __init__(self, predicate) -> None: +class OnNextPredicate(Generic[_T]): + def __init__(self, predicate: typing.Predicate[_T]) -> None: self.predicate = predicate - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if other == self: return True if other is None: @@ -37,11 +40,11 @@ def __eq__(self, other): return self.predicate(other.value) -class OnErrorPredicate: - def __init__(self, predicate): +class OnErrorPredicate(Generic[_T]): + def __init__(self, predicate: typing.Predicate[_T]): self.predicate = predicate - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if other == self: return True if other is None: @@ -57,21 +60,21 @@ class ReactiveTest: disposed = 1000 @staticmethod - def on_next(ticks: int, value: Any) -> Recorded: + def on_next(ticks: int, value: _T) -> Recorded[_T]: if isinstance(value, types.FunctionType): return Recorded(ticks, OnNextPredicate(value)) return Recorded(ticks, OnNext(value)) @staticmethod - def on_error(ticks: int, exception: Exception) -> Recorded: + def on_error(ticks: int, exception: Union[Exception, str]) -> Recorded[Any]: if isinstance(exception, types.FunctionType): return Recorded(ticks, OnErrorPredicate(exception)) return Recorded(ticks, OnError(exception)) @staticmethod - def on_completed(ticks: int) -> Recorded: + def on_completed(ticks: int) -> Recorded[Any]: return Recorded(ticks, OnCompleted()) @staticmethod diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index 51be0fb01..58d3bc2b3 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -1,15 +1,17 @@ -from typing import Any, Callable, TypeVar +from typing import Any, Callable, List, Optional, TypeVar, Union, cast import rx from rx.core import Observable, abc, typing from rx.disposable import Disposable from rx.scheduler import VirtualTimeScheduler +from rx.testing.recorded import Recorded from .coldobservable import ColdObservable from .hotobservable import HotObservable from .mockobserver import MockObserver from .reactivetest import ReactiveTest +_T = TypeVar("_T") _TState = TypeVar("_TState") @@ -49,7 +51,13 @@ def add(cls, absolute, relative): return absolute + relative - def start(self, create=None, created=None, subscribed=None, disposed=None) -> MockObserver: # type: ignore + def start( + self, + create: Optional[Callable[[], Observable[_T]]] = None, + created: Optional[int] = None, + subscribed: Optional[int] = None, + disposed: Optional[int] = None, + ) -> MockObserver[_T]: """Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription. @@ -79,7 +87,7 @@ def start(self, create=None, created=None, subscribed=None, disposed=None) -> Mo subscription = [None] source = [None] - def action_create(scheduler, state): + def action_create(scheduler: abc.SchedulerBase, state: Any = None): """Called at create time. Defaults to 100""" source[0] = create() return Disposable() @@ -103,7 +111,9 @@ def action_dispose(scheduler, state): super().start() return observer - def create_hot_observable(self, *args) -> Observable: + def create_hot_observable( + self, *args: Union[Recorded[_T], List[Recorded[_T]]] + ) -> HotObservable[_T]: """Creates a hot observable using the specified timestamped notification messages either as a list or by multiple arguments. @@ -115,13 +125,15 @@ def create_hot_observable(self, *args) -> Observable: of subscriptions and notifications. """ - if args and isinstance(args[0], list): + if args and isinstance(args[0], List): messages = args[0] else: - messages = list(args) + messages = cast(List[Recorded[_T]], list(args)) return HotObservable(self, messages) - def create_cold_observable(self, *args) -> Observable: + def create_cold_observable( + self, *args: Union[Recorded[_T], List[Recorded[_T]]] + ) -> Observable[_T]: """Creates a cold observable using the specified timestamped notification messages either as an array or arguments. @@ -138,10 +150,10 @@ def create_cold_observable(self, *args) -> Observable: if args and isinstance(args[0], list): messages = args[0] else: - messages = list(args) + messages = cast(List[Recorded[_T]], list(args)) return ColdObservable(self, messages) - def create_observer(self) -> MockObserver: + def create_observer(self) -> MockObserver[Any]: """Creates an observer that records received notification messages and timestamps those. Return an Observer that can be used to assert the timing of received notifications. diff --git a/tests/test_observable/test_filter.py b/tests/test_observable/test_filter.py index ece945317..470d5280d 100644 --- a/tests/test_observable/test_filter.py +++ b/tests/test_observable/test_filter.py @@ -1,4 +1,5 @@ import unittest +from rx import Observable from rx.testing import TestScheduler, ReactiveTest, is_prime from rx.disposable import SerialDisposable @@ -18,11 +19,6 @@ class RxException(Exception): pass -# Helper function for raising exceptions within lambdas -def _raise(ex): - raise RxException(ex) - - def test_is_prime(): assert not is_prime(1) assert is_prime(2) @@ -50,10 +46,11 @@ def test_filter_complete(self): on_next(580, 11), on_completed(600), on_next(610, 12), - on_error(620, 'ex'), - on_completed(630)) + on_error(620, "ex"), + on_completed(630), + ) - def create(): + def create() -> Observable[int]: def predicate(x): invoked[0] += 1 return is_prime(x) @@ -62,11 +59,13 @@ def predicate(x): results = scheduler.start(create) - assert results.messages == [on_next(230, 3), - on_next(340, 5), - on_next(390, 7), - on_next(580, 11), - on_completed(600)] + assert results.messages == [ + on_next(230, 3), + on_next(340, 5), + on_next(390, 7), + on_next(580, 11), + on_completed(600), + ] assert xs.subscriptions == [subscribe(200, 600)] assert invoked[0] == 9 @@ -85,28 +84,52 @@ def test_filter_true(self): on_next(470, 9), on_next(560, 10), on_next(580, 11), - on_completed(600)) + on_completed(600), + ) - def create(): - def predicate(x): + def create() -> Observable[int]: + def predicate(x: int) -> bool: invoked[0] += 1 return True + return xs.pipe(filter(predicate)) results = scheduler.start(create) - assert results.messages == [on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)] + assert results.messages == [ + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ] assert xs.subscriptions == [subscribe(200, 600)] assert invoked[0] == 9 def test_filter_false(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next( - 380, 6), on_next(390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ) - def create(): - def predicate(x): + def create() -> Observable[int]: + def predicate(x: int) -> bool: invoked[0] += 1 return False @@ -121,25 +144,37 @@ def predicate(x): def test_filter_dispose(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next( - 380, 6), on_next(390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ) def create(): def predicate(x): invoked[0] += 1 return is_prime(x) + return xs.pipe(filter(predicate)) results = scheduler.start(create, disposed=400) - assert results.messages == [ - on_next(230, 3), on_next(340, 5), on_next(390, 7)] + assert results.messages == [on_next(230, 3), on_next(340, 5), on_next(390, 7)] assert xs.subscriptions == [subscribe(200, 400)] assert invoked[0] == 5 def test_filter_error(self): scheduler = TestScheduler() invoked = [0] - ex = 'ex' + ex = "ex" xs = scheduler.create_hot_observable( on_next(110, 1), on_next(180, 2), @@ -154,28 +189,50 @@ def test_filter_error(self): on_next(580, 11), on_error(600, ex), on_next(610, 12), - on_error(620, 'ex'), - on_completed(630)) + on_error(620, "ex"), + on_completed(630), + ) def create(): def predicate(x): invoked[0] += 1 return is_prime(x) + return xs.pipe(filter(predicate)) results = scheduler.start(create) - assert results.messages == [on_next(230, 3), on_next( - 340, 5), on_next(390, 7), on_next(580, 11), on_error(600, ex)] + assert results.messages == [ + on_next(230, 3), + on_next(340, 5), + on_next(390, 7), + on_next(580, 11), + on_error(600, ex), + ] assert xs.subscriptions == [subscribe(200, 600)] - assert(invoked[0] == 9) + assert invoked[0] == 9 def test_filter_on_error(self): scheduler = TestScheduler() invoked = [0] - ex = 'ex' - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + ex = "ex" + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) def create(): def predicate(x): @@ -184,32 +241,47 @@ def predicate(x): raise Exception(ex) return is_prime(x) + return xs.pipe(filter(predicate)) results = scheduler.start(create) - assert results.messages == [ - on_next(230, 3), on_next(340, 5), on_error(380, ex)] + assert results.messages == [on_next(230, 3), on_next(340, 5), on_error(380, ex)] assert xs.subscriptions == [subscribe(200, 380)] - assert(invoked[0] == 4) + assert invoked[0] == 4 def test_filter_dispose_in_predicate(self): scheduler = TestScheduler() invoked = [0] ys = [None] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) results = scheduler.create_observer() d = SerialDisposable() def action(scheduler, state): - def predicate(x): invoked[0] += 1 if x == 8: d.dispose() return is_prime(x) + ys[0] = xs.pipe(filter(predicate)) return ys[0] @@ -226,16 +298,30 @@ def action2(scheduler, state): scheduler.schedule_absolute(disposed, action2) scheduler.start() - assert results.messages == [ - on_next(230, 3), on_next(340, 5), on_next(390, 7)] + assert results.messages == [on_next(230, 3), on_next(340, 5), on_next(390, 7)] assert xs.subscriptions == [subscribe(200, 450)] - assert(invoked[0] == 6) + assert invoked[0] == 6 def test_filter_indexed_complete(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) def create(): def predicate(x, index): @@ -245,16 +331,27 @@ def predicate(x, index): return xs.pipe(filter_indexed(predicate)) results = scheduler.start(create) - assert results.messages == [ - on_next(230, 3), on_next(390, 7), on_completed(600)] + assert results.messages == [on_next(230, 3), on_next(390, 7), on_completed(600)] assert xs.subscriptions == [subscribe(200, 600)] - assert(invoked[0] == 9) + assert invoked[0] == 9 def test_filter_indexed_true(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next( - 380, 6), on_next(390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ) def create(): def predicate(x, index): @@ -264,34 +361,69 @@ def predicate(x, index): return xs.pipe(filter_indexed(predicate)) results = scheduler.start(create) - assert results.messages == [on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)] + assert results.messages == [ + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ] assert xs.subscriptions == [subscribe(200, 600)] - assert(invoked[0] == 9) + assert invoked[0] == 9 def test_filter_indexed_false(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next( - 380, 6), on_next(390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ) def create(): def predicate(x, index): invoked[0] += 1 return False + return xs.pipe(filter_indexed(predicate)) results = scheduler.start(create) assert results.messages == [on_completed(600)] assert xs.subscriptions == [subscribe(200, 600)] - assert(invoked[0] == 9) + assert invoked[0] == 9 def test_filter_indexed_dispose(self): scheduler = TestScheduler() invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next( - 380, 6), on_next(390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + ) def create(): def predicate(x, index): @@ -303,34 +435,64 @@ def predicate(x, index): results = scheduler.start(create, disposed=400) assert results.messages == [on_next(230, 3), on_next(390, 7)] assert xs.subscriptions == [subscribe(200, 400)] - assert(invoked[0] == 5) + assert invoked[0] == 5 def test_filter_indexed_error(self): scheduler = TestScheduler() invoked = [0] - ex = 'ex' - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_error(600, ex), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + ex = "ex" + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_error(600, ex), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) def create(): def predicate(x, index): invoked[0] += 1 return is_prime(x + index * 10) + return xs.pipe(filter_indexed(predicate)) results = scheduler.start(create) - assert results.messages == [ - on_next(230, 3), on_next(390, 7), on_error(600, ex)] + assert results.messages == [on_next(230, 3), on_next(390, 7), on_error(600, ex)] assert xs.subscriptions == [subscribe(200, 600)] - assert(invoked[0] == 9) + assert invoked[0] == 9 def test_filter_indexed_on_error(self): scheduler = TestScheduler() invoked = [0] - ex = 'ex' - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + ex = "ex" + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) def create(): def predicate(x, index): @@ -339,19 +501,35 @@ def predicate(x, index): raise Exception(ex) return is_prime(x + index * 10) + return xs.pipe(filter_indexed(predicate)) results = scheduler.start(create) assert results.messages == [on_next(230, 3), on_error(380, ex)] assert xs.subscriptions == [subscribe(200, 380)] - assert(invoked[0] == 4) + assert invoked[0] == 4 def test_filter_indexed_dispose_in_predicate(self): scheduler = TestScheduler() ys = [None] invoked = [0] - xs = scheduler.create_hot_observable(on_next(110, 1), on_next(180, 2), on_next(230, 3), on_next(270, 4), on_next(340, 5), on_next(380, 6), on_next( - 390, 7), on_next(450, 8), on_next(470, 9), on_next(560, 10), on_next(580, 11), on_completed(600), on_next(610, 12), on_error(620, 'ex'), on_completed(630)) + xs = scheduler.create_hot_observable( + on_next(110, 1), + on_next(180, 2), + on_next(230, 3), + on_next(270, 4), + on_next(340, 5), + on_next(380, 6), + on_next(390, 7), + on_next(450, 8), + on_next(470, 9), + on_next(560, 10), + on_next(580, 11), + on_completed(600), + on_next(610, 12), + on_error(620, "ex"), + on_completed(630), + ) results = scheduler.create_observer() d = SerialDisposable() @@ -362,6 +540,7 @@ def predicate(x, index): d.dispose() return is_prime(x + index * 10) + ys[0] = xs.pipe(filter_indexed(predicate)) scheduler.schedule_absolute(created, action1) diff --git a/tests/test_observable/test_slice.py b/tests/test_observable/test_slice.py index 056785707..ce00c3c66 100644 --- a/tests/test_observable/test_slice.py +++ b/tests/test_observable/test_slice.py @@ -1,4 +1,5 @@ import unittest +from rx.core.observable.observable import Observable from rx.testing import TestScheduler, ReactiveTest @@ -12,13 +13,12 @@ class TestSlice(unittest.TestCase): - def test_slice_empty(self): scheduler = TestScheduler() msgs = [on_next(150, 1), on_completed(250)] xs = scheduler.create_hot_observable(msgs) - def create(): + def create() -> Observable[int]: return xs[1:42] res = scheduler.start(create=create).messages @@ -39,10 +39,12 @@ def test_slice_same(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[0:10] + results = scheduler.start(create) assert results.messages == [ on_next(210, 0), @@ -55,7 +57,8 @@ def create(): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(415)] + on_completed(415), + ] assert xs.subscriptions == [subscribe(200, 415)] def test_slice_same_noop(self): @@ -73,10 +76,12 @@ def test_slice_same_noop(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[:] + results = scheduler.start(create) assert results.messages == [ on_next(210, 0), @@ -89,7 +94,8 @@ def create(): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)] + on_completed(690), + ] assert xs.subscriptions == [subscribe(200, 690)] def test_slice_skip_first(self): @@ -107,10 +113,12 @@ def test_slice_skip_first(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[2:] + results = scheduler.start(create) assert results.messages == [ on_next(270, 2), @@ -121,7 +129,8 @@ def create(): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)] + on_completed(690), + ] assert xs.subscriptions == [subscribe(200, 690)] def test_slice_skip_last(self): @@ -139,10 +148,12 @@ def test_slice_skip_last(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[:-2] + results = scheduler.start(create) assert results.messages == [ on_next(270, 0), @@ -153,7 +164,8 @@ def create(): on_next(370, 5), on_next(410, 6), on_next(415, 7), - on_completed(690)] + on_completed(690), + ] assert xs.subscriptions == [subscribe(200, 690)] def test_slice_take_last(self): @@ -171,15 +183,14 @@ def test_slice_take_last(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[-2:] + results = scheduler.start(create) - assert results.messages == [ - on_next(690, 8), - on_next(690, 9), - on_completed(690)] + assert results.messages == [on_next(690, 8), on_next(690, 9), on_completed(690)] assert xs.subscriptions == [subscribe(200, 690)] def test_slice_take_first(self): @@ -197,15 +208,14 @@ def test_slice_take_first(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[:2] + results = scheduler.start(create) - assert results.messages == [ - on_next(210, 0), - on_next(230, 1), - on_completed(230)] + assert results.messages == [on_next(210, 0), on_next(230, 1), on_completed(230)] assert xs.subscriptions == [subscribe(200, 230)] def test_slice_take_last_skip_all(self): @@ -223,13 +233,14 @@ def test_slice_take_last_skip_all(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[-2:0] + results = scheduler.start(create) - assert results.messages == [ - on_completed(200)] + assert results.messages == [on_completed(200)] def test_slice_step_2(self): scheduler = TestScheduler() @@ -246,10 +257,12 @@ def test_slice_step_2(self): on_next(370, 7), on_next(410, 8), on_next(415, 9), - on_completed(690)) + on_completed(690), + ) def create(): return xs[0:10:2] + results = scheduler.start(create) assert results.messages == [ on_next(210, 0), @@ -257,5 +270,6 @@ def create(): on_next(300, 4), on_next(340, 6), on_next(410, 8), - on_completed(415)] + on_completed(415), + ] assert xs.subscriptions == [subscribe(200, 415)] diff --git a/tests/test_scheduler/test_scheduler.py b/tests/test_scheduler/test_scheduler.py index 09633a820..42b76e213 100644 --- a/tests/test_scheduler/test_scheduler.py +++ b/tests/test_scheduler/test_scheduler.py @@ -1,12 +1,10 @@ import unittest -from datetime import timedelta from rx.internal.constants import DELTA_ZERO, UTC_ZERO from rx.scheduler.scheduler import Scheduler class TestScheduler(unittest.TestCase): - def test_base_to_seconds(self): val = Scheduler.to_seconds(0.0) assert val == 0.0 From 2b5f6d24e186df2d1c73cb4ccb59f8767de9c2cc Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 14:26:04 +0100 Subject: [PATCH 025/103] Formatting --- rx/core/observable/groupedobservable.py | 5 +++-- rx/core/operators/groupby.py | 2 +- rx/core/operators/groupbyuntil.py | 2 +- rx/testing/reactivetest.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rx/core/observable/groupedobservable.py b/rx/core/observable/groupedobservable.py index 5b85e97c4..170081912 100644 --- a/rx/core/observable/groupedobservable.py +++ b/rx/core/observable/groupedobservable.py @@ -1,6 +1,7 @@ -from typing import TypeVar, Optional, Generic -from rx.disposable import CompositeDisposable, RefCountDisposable, Disposable +from typing import Generic, Optional, TypeVar + from rx.core import abc +from rx.disposable import CompositeDisposable, Disposable, RefCountDisposable from .observable import Observable diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index 50328edb3..40ef08f32 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Any +from typing import Any, Callable, Optional, TypeVar import rx from rx import operators as ops diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index a3fe4b5db..f30d928b6 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import Callable, Optional, TypeVar, Any, Type +from typing import Any, Callable, Optional, Type, TypeVar from rx import operators as ops from rx.core import GroupedObservable, Observable, abc diff --git a/rx/testing/reactivetest.py b/rx/testing/reactivetest.py index 17eac67f1..ae511beb3 100644 --- a/rx/testing/reactivetest.py +++ b/rx/testing/reactivetest.py @@ -2,8 +2,8 @@ import types from typing import Any, Generic, TypeVar, Union -from rx.core.notification import OnCompleted, OnError, OnNext from rx.core import typing +from rx.core.notification import OnCompleted, OnError, OnNext from .recorded import Recorded from .subscription import Subscription From e77ddab7b8dc0a49d151e1b1488b9a3c7fc50b72 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 14:36:04 +0100 Subject: [PATCH 026/103] Fix flaky test --- tests/test_scheduler/test_eventloop/test_asyncioscheduler.py | 3 +-- tests/test_scheduler/test_trampolinescheduler.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py b/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py index 2460955dd..91c456763 100644 --- a/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py +++ b/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py @@ -7,12 +7,11 @@ class TestAsyncIOScheduler(unittest.TestCase): - def test_asyncio_schedule_now(self): loop = asyncio.get_event_loop() scheduler = AsyncIOScheduler(loop) diff = scheduler.now - datetime.utcfromtimestamp(loop.time()) - assert abs(diff) < timedelta(milliseconds=1) + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI def test_asyncio_schedule_now_units(self): loop = asyncio.get_event_loop() diff --git a/tests/test_scheduler/test_trampolinescheduler.py b/tests/test_scheduler/test_trampolinescheduler.py index 4d6a2284d..2d13c64ef 100644 --- a/tests/test_scheduler/test_trampolinescheduler.py +++ b/tests/test_scheduler/test_trampolinescheduler.py @@ -12,7 +12,7 @@ class TestTrampolineScheduler(unittest.TestCase): def test_trampoline_now(self): scheduler = TrampolineScheduler() diff = scheduler.now - default_now() - assert abs(diff) < timedelta(milliseconds=2) # NOTE: diff can be 1 ms in CI + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI def test_trampoline_now_units(self): scheduler = TrampolineScheduler() From 5e6212848f012e2f3ae37e68f453d0003aba3602 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 15:00:35 +0100 Subject: [PATCH 027/103] Fix types --- rx/testing/recorded.py | 5 +++-- rx/testing/testscheduler.py | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index 05309c333..8c6d0b98c 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,7 +1,8 @@ -from typing import Any, Generic, Optional, TypeVar, cast +from typing import Any, Generic, Optional, TypeVar, Union, cast from rx.core import Notification, typing from rx.internal.basic import default_comparer +from rx.testing.reactivetest import OnErrorPredicate, OnNextPredicate _T = TypeVar("_T") @@ -10,7 +11,7 @@ class Recorded(Generic[_T]): def __init__( self, time: int, - value: Notification[_T], + value: Union[Notification[_T], OnNextPredicate[_T], OnErrorPredicate[_T]], comparer: Optional[typing.Comparer[_T]] = None, ): self.time = time diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index 58d3bc2b3..dd8c97b0b 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -84,8 +84,8 @@ def start( disposed = disposed or ReactiveTest.disposed observer = self.create_observer() - subscription = [None] - source = [None] + subscription: List[Optional[abc.DisposableBase]] = [None] + source: List[Optional[abc.ObservableBase[_T]]] = [None] def action_create(scheduler: abc.SchedulerBase, state: Any = None): """Called at create time. Defaults to 100""" @@ -94,15 +94,17 @@ def action_create(scheduler: abc.SchedulerBase, state: Any = None): self.schedule_absolute(created, action_create) - def action_subscribe(scheduler, state): + def action_subscribe(scheduler: abc.SchedulerBase, state: Any = None): """Called at subscribe time. Defaults to 200""" + assert source[0] subscription[0] = source[0].subscribe(observer, scheduler=scheduler) return Disposable() self.schedule_absolute(subscribed, action_subscribe) - def action_dispose(scheduler, state): + def action_dispose(scheduler: abc.SchedulerBase, state: Any = None): """Called at dispose time. Defaults to 1000""" + assert subscription[0] subscription[0].dispose() return Disposable() From cd4cb6156f50dffd77f4f1423718db04982a1325 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 15:00:51 +0100 Subject: [PATCH 028/103] Speed up CI --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 4f41defdb..7b249cc20 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -31,7 +31,7 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - pytest + pytest -n auto coverage: runs-on: ubuntu-latest From 01868c9622ee8d7da436bf4b6ccba919c9818d44 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 15:02:21 +0100 Subject: [PATCH 029/103] Add missing requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 029062be9..22375d981 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ pytest +pytest-xdist coveralls flake8 black From 4bb32bbadaa5814c0149beafc20195d9ab8b8426 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 15:06:05 +0100 Subject: [PATCH 030/103] Remove circular import --- rx/testing/recorded.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index 8c6d0b98c..8aab58817 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,8 +1,11 @@ -from typing import Any, Generic, Optional, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast from rx.core import Notification, typing from rx.internal.basic import default_comparer -from rx.testing.reactivetest import OnErrorPredicate, OnNextPredicate + +if TYPE_CHECKING: + from rx.testing.reactivetest import OnErrorPredicate, OnNextPredicate + _T = TypeVar("_T") @@ -11,7 +14,7 @@ class Recorded(Generic[_T]): def __init__( self, time: int, - value: Union[Notification[_T], OnNextPredicate[_T], OnErrorPredicate[_T]], + value: Union[Notification[_T], "OnNextPredicate[_T]", "OnErrorPredicate[_T]"], comparer: Optional[typing.Comparer[_T]] = None, ): self.time = time From edd1ac84ea8a8bdf7648b34e2027c3873e4a9c07 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 15:12:23 +0100 Subject: [PATCH 031/103] Fix flaky test --- tests/test_scheduler/test_immediatescheduler.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_scheduler/test_immediatescheduler.py b/tests/test_scheduler/test_immediatescheduler.py index 85ad56a21..6ddef6c38 100644 --- a/tests/test_scheduler/test_immediatescheduler.py +++ b/tests/test_scheduler/test_immediatescheduler.py @@ -13,12 +13,8 @@ class TestImmediateScheduler(unittest.TestCase): - def test_immediate_singleton(self): - scheduler = [ - ImmediateScheduler(), - ImmediateScheduler.singleton() - ] + scheduler = [ImmediateScheduler(), ImmediateScheduler.singleton()] assert scheduler[0] is scheduler[1] gate = [threading.Semaphore(0), threading.Semaphore(0)] @@ -51,7 +47,7 @@ class MyScheduler(ImmediateScheduler): def test_immediate_now(self): scheduler = ImmediateScheduler() diff = scheduler.now - default_now() - assert abs(diff) <= timedelta(milliseconds=1) + assert abs(diff) <= timedelta(milliseconds=2) # NOTE: may be 1 ms in CI def test_immediate_now_units(self): scheduler = ImmediateScheduler() From a4b5ee9710225e74fcea0948df703b42386ef25c Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 17:18:24 +0100 Subject: [PATCH 032/103] Fixes for flat_map, fork_join and switch_latest --- examples/autocomplete/autocomplete.py | 25 ++++++++++++++----------- rx/core/observable/forkjoin.py | 17 ++++++++++------- rx/core/observable/observable.py | 1 - rx/core/operators/flatmap.py | 5 +++-- rx/core/operators/forkjoin.py | 20 +++++++++++++------- rx/core/operators/switchlatest.py | 8 ++++++-- rx/operators/__init__.py | 14 +++++++++----- 7 files changed, 55 insertions(+), 35 deletions(-) diff --git a/examples/autocomplete/autocomplete.py b/examples/autocomplete/autocomplete.py index 559494fcb..b6e69d1ff 100644 --- a/examples/autocomplete/autocomplete.py +++ b/examples/autocomplete/autocomplete.py @@ -6,24 +6,25 @@ Uses the RxPY IOLoopScheduler. """ +from asyncio import Future import os -from typing import Any, Dict +from typing import Any, Dict, Union from tornado.websocket import WebSocketHandler from tornado.web import RequestHandler, StaticFileHandler, Application, url -from tornado.httpclient import AsyncHTTPClient +from tornado.httpclient import AsyncHTTPClient, HTTPResponse from tornado.httputil import url_concat from tornado.escape import json_decode from tornado import ioloop -from rx import Observable, operators as ops +from rx import operators as ops from rx.subject import Subject from rx.scheduler.eventloop import IOLoopScheduler scheduler = IOLoopScheduler(ioloop.IOLoop.current()) -def search_wikipedia(term): +def search_wikipedia(term: str) -> Future[HTTPResponse]: """Search Wikipedia for a given term""" url = "http://en.wikipedia.org/w/api.php" @@ -45,10 +46,10 @@ def open(self, *args: Any): # A Subject is both an observable and observer, so we can both subscribe # to it and also feed (send) it with new values - self.stream: Subject[Dict[str, str]] = Subject() + self.subject: Subject[Dict[str, str]] = Subject() # Get all distinct key up events from the input and only fire if long enough and distinct - searcher = self.stream.pipe( + searcher = self.subject.pipe( ops.map(lambda x: x["term"]), ops.filter( lambda text: len(text) > 2 @@ -58,17 +59,19 @@ def open(self, *args: Any): ops.flat_map_latest(search_wikipedia), ) - def send_response(x): + def send_response(x: HTTPResponse) -> None: self.write_message(x.body) - def on_error(ex: Exception): + def on_error(ex: Exception) -> None: print(ex) - searcher.subscribe(send_response, on_error, scheduler=scheduler) + searcher.subscribe_( + on_next=send_response, on_error=on_error, scheduler=scheduler + ) - def on_message(self, message): + def on_message(self, message: Union[bytes, str]): obj = json_decode(message) - self.stream.on_next(obj) + self.subject.on_next(obj) def on_close(self): print("WebSocket closed") diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index b3e6ab50f..b0b9df287 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -1,10 +1,10 @@ -from typing import Optional +from typing import List, Optional, Any, Tuple, cast from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -def fork_join_(*sources: Observable) -> Observable: +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. @@ -20,8 +20,9 @@ def fork_join_(*sources: Observable) -> Observable: parent = sources[0] def subscribe( - observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None - ) -> CompositeDisposable: + observer: abc.ObserverBase[Tuple[Any, ...]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: n = len(sources) values = [None] * n is_done = [False] * n @@ -41,17 +42,19 @@ def done(i: int): else: observer.on_completed() - subscriptions = [None] * n + subscriptions: List[SingleAssignmentDisposable] = [ + cast(SingleAssignmentDisposable, None) + ] * n def _subscribe(i: int): subscriptions[i] = SingleAssignmentDisposable() - def on_next(value): + def on_next(value: Any) -> None: with parent.lock: values[i] = value has_value[i] = True - def on_completed(): + def on_completed() -> None: with parent.lock: done(i) diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 26276f77b..5ceb2a345 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -82,7 +82,6 @@ def subscribe( it has a (callable) attribute named :code:`on_next`, then any callback arguments will be ignored. - Examples: >>> source.subscribe() >>> source.subscribe(observer) diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index 8e023dd61..b0a5317b0 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -1,4 +1,5 @@ -from typing import Callable, Iterable, Optional, TypeVar +from asyncio import Future +from typing import Callable, Iterable, Optional, TypeVar, Union from rx import from_, from_future from rx import operators as ops @@ -93,7 +94,7 @@ def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: def flat_map_latest_( - mapper: Mapper[_T1, Observable[_T2]] + mapper: Mapper[_T1, Union[Observable[_T2], "Future[_T2]"]] ) -> Callable[[Observable[_T1]], Observable[_T2]]: def flat_map_latest(source: Observable[_T1]) -> Observable[_T2]: """Projects each element of an observable sequence into a new diff --git a/rx/core/operators/forkjoin.py b/rx/core/operators/forkjoin.py index 8ce3ca90a..20f4e6896 100644 --- a/rx/core/operators/forkjoin.py +++ b/rx/core/operators/forkjoin.py @@ -1,22 +1,28 @@ -from typing import Callable +from typing import Callable, Tuple, Any import rx from rx import Observable -def _fork_join(*args: Observable) -> Callable[[Observable], Observable]: - def fork_join(source: Observable) -> Observable: +def fork_join_( + *args: Observable[Any], +) -> Callable[[Observable[Any]], Observable[Tuple[Any, ...]]]: + def fork_join(source: 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. + 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 = fork_join(source) Returns: - An observable sequence containing the result of combining last element from - each source in given sequence. + An observable sequence containing the result of combining + last element from each source in given sequence. """ return rx.fork_join(source, *args) return fork_join + + +__all__ = ["fork_join_"] diff --git a/rx/core/operators/switchlatest.py b/rx/core/operators/switchlatest.py index 6a8f10542..e88b0844f 100644 --- a/rx/core/operators/switchlatest.py +++ b/rx/core/operators/switchlatest.py @@ -12,8 +12,12 @@ _T = TypeVar("_T") -def switch_latest_() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: - def switch_latest(source: Observable[Observable[_T]]) -> Observable[_T]: +def switch_latest_() -> Callable[ + [Observable[Union[Observable[_T], "Future[_T]"]]], Observable[_T] +]: + def switch_latest( + source: Observable[Union[Observable[_T], "Future[_T]"]] + ) -> Observable[_T]: """Partially applied switch_latest operator. Transforms an observable sequence of observable sequences into diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 4bca3bea2..60c7a7024 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1273,7 +1273,7 @@ def flat_map_indexed( def flat_map_latest( - mapper: Mapper[_T1, Observable[_T2]] + mapper: Mapper[_T1, Union[Observable[_T2], "Future[_T2]"]] ) -> Callable[[Observable[_T1]], Observable[_T2]]: """Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's @@ -1299,7 +1299,9 @@ def flat_map_latest( return flat_map_latest_(mapper) -def fork_join(*others: Observable) -> Callable[[Observable], Observable]: +def fork_join( + *others: Observable[Any], +) -> Callable[[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. @@ -1322,9 +1324,9 @@ def fork_join(*others: Observable) -> Callable[[Observable], Observable]: return an observable sequence containing the result of combining last element from each source in given sequence. """ - from rx.core.operators.forkjoin import _fork_join + from rx.core.operators.forkjoin import fork_join_ - return _fork_join(*others) + return fork_join_(*others) def group_by( @@ -3002,7 +3004,9 @@ def sum( return sum_(key_mapper) -def switch_latest() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: +def switch_latest() -> Callable[ + [Observable[Union[Observable[_T], "Future[_T]"]]], Observable[_T] +]: """The switch_latest operator. Transforms an observable sequence of observable sequences into an From d378e074174decb394e2227f5931f4ef52c4a923 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 6 Feb 2022 17:18:58 +0100 Subject: [PATCH 033/103] Fix import sorting --- rx/core/observable/forkjoin.py | 2 +- rx/core/operators/forkjoin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index b0b9df287..dcf73e7e3 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Any, Tuple, cast +from typing import Any, List, Optional, Tuple, cast from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable diff --git a/rx/core/operators/forkjoin.py b/rx/core/operators/forkjoin.py index 20f4e6896..87060bf84 100644 --- a/rx/core/operators/forkjoin.py +++ b/rx/core/operators/forkjoin.py @@ -1,4 +1,4 @@ -from typing import Callable, Tuple, Any +from typing import Any, Callable, Tuple import rx from rx import Observable From a485f89c38437504352300cd2d7838e6589adccf Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 7 Feb 2022 08:56:55 +0100 Subject: [PATCH 034/103] Fix flatmap overloads --- rx/__init__.py | 4 +- rx/core/observable/range.py | 3 +- rx/core/operators/flatmap.py | 23 +++++----- rx/operators/__init__.py | 86 +++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 15 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index df3c95c36..f111d33f1 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -31,7 +31,6 @@ _F = TypeVar("_F") _G = TypeVar("_G") - # Please make sure the version here remains the same as in project.cfg __version__ = "3.2.0" @@ -929,7 +928,8 @@ def range( Args: start: The value of the first integer in the sequence. - count: The number of sequential integers to generate. + 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: [Optional] The scheduler to schedule the values on. If not specified, the default is to use an instance of :class:`CurrentThreadScheduler `. diff --git a/rx/core/observable/range.py b/rx/core/observable/range.py index 80dcc70c7..fdcd50d65 100644 --- a/rx/core/observable/range.py +++ b/rx/core/observable/range.py @@ -23,7 +23,8 @@ def range_( Args: start: The value of the first integer in the sequence. - count: The number of sequential integers to generate. + 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: diff --git a/rx/core/operators/flatmap.py b/rx/core/operators/flatmap.py index b0a5317b0..f210c24ca 100644 --- a/rx/core/operators/flatmap.py +++ b/rx/core/operators/flatmap.py @@ -1,11 +1,10 @@ from asyncio import Future -from typing import Callable, Iterable, Optional, TypeVar, Union +from typing import Any, Callable, Iterable, Optional, TypeVar, Union, cast from rx import from_, from_future from rx import operators as ops from rx.core import Observable from rx.core.typing import Mapper, MapperIndexed -from rx.internal.utils import is_future _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") @@ -13,19 +12,21 @@ def _flat_map_internal( source: Observable[_T1], - mapper: Optional[Mapper[_T1, Observable[_T2]]] = None, - mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, -) -> Observable[_T2]: + mapper: Optional[Mapper[_T1, Any]] = None, + mapper_indexed: Optional[MapperIndexed[_T1, Any]] = None, +) -> Observable[Any]: def projection(x: _T1, i: int): mapper_result = ( mapper(x) if mapper else mapper_indexed(x, i) if mapper_indexed else None ) - if is_future(mapper_result): - result = from_future(mapper_result) + if isinstance(mapper_result, Future): + result: Observable[Any] = from_future(cast("Future[Any]", mapper_result)) elif isinstance(mapper_result, Iterable): result = from_(mapper_result) - else: + elif isinstance(mapper_result, Observable): result = mapper_result + else: + result = from_(mapper_result) return result return source.pipe(ops.map_indexed(projection), ops.merge_all()) @@ -64,9 +65,9 @@ def flat_map(source: Observable[_T1]) -> Observable[_T2]: def flat_map_indexed_( - mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, -) -> Callable[[Observable[_T1]], Observable[_T2]]: - def flat_map_indexed(source: Observable[_T1]) -> Observable[_T2]: + mapper_indexed: Optional[Any] = None, +) -> Callable[[Observable[Any]], Observable[Any]]: + def flat_map_indexed(source: Observable[Any]) -> Observable[Any]: """One of the Following: Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 05cb1517b..11c8016e1 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -15,6 +15,7 @@ TypeVar, Union, cast, + overload, ) from rx.core import ( @@ -1183,9 +1184,51 @@ def first_or_default( return first_or_default_(predicate, default_value) +@overload +def flat_map( + mapper: Optional["Future[_T2]"] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map( + mapper: Optional[Iterable[_T2]] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map( + mapper: Optional[Observable[_T2]] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map( + mapper: Optional[Mapper[_T1, "Future[_T2]"]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +@overload +def flat_map( + mapper: Optional[Mapper[_T1, Iterable[_T2]]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +@overload def flat_map( mapper: Optional[Mapper[_T1, Observable[_T2]]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +def flat_map( + mapper: Optional[Any] = None, +) -> Callable[[Observable[Any]], Observable[Any]]: """The flat_map operator. .. marble:: @@ -1227,10 +1270,51 @@ def flat_map( return flat_map_(mapper) +@overload +def flat_map_indexed( + mapper_indexed: Optional["Future[_T2]"] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map_indexed( + mapper_indexed: Optional[Iterable[_T2]] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map_indexed( + mapper_indexed: Optional[Observable[_T2]] = None, +) -> Callable[[Observable[Any]], Observable[_T2]]: + ... + + +@overload +def flat_map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, "Future[_T2]"]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +@overload +def flat_map_indexed( + mapper_indexed: Optional[MapperIndexed[_T1, Iterable[_T2]]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + +@overload def flat_map_indexed( - mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None, + mapper_indexed: Optional[MapperIndexed[_T1, Observable[_T2]]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +def flat_map_indexed( + mapper_indexed: Any = None, +) -> Callable[[Observable[Any]], Observable[Any]]: """The `flat_map_indexed` operator. One of the Following: From 2c4da5803c1fe9ca5335195f6722109d6e81e063 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 7 Feb 2022 09:00:01 +0100 Subject: [PATCH 035/103] Add missing empty line --- rx/operators/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 11c8016e1..3498b4d22 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1270,6 +1270,7 @@ def flat_map( return flat_map_(mapper) + @overload def flat_map_indexed( mapper_indexed: Optional["Future[_T2]"] = None, From 8e0abc23f9f9ad0900f26eea5846a4945e90b39e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 7 Feb 2022 09:48:59 +0100 Subject: [PATCH 036/103] Make pipe arguments anonymous --- rx/core/observable/observable.py | 60 +++++++++++++++----------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 5ceb2a345..322f3d0f8 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -231,55 +231,55 @@ def pipe( @overload def pipe( self, - op1: Callable[[Observable[_T]], _A], - op2: Callable[[_A], _B], - op3: Callable[[_B], _C], + __op1: Callable[[Observable[_T]], _A], + __op2: Callable[[_A], _B], + __op3: Callable[[_B], _C], ) -> _C: ... @overload def pipe( self, - op1: Callable[[Observable[_T]], _A], - op2: Callable[[_A], _B], - op3: Callable[[_B], _C], - op4: Callable[[_C], _D], + __op1: Callable[[Observable[_T]], _A], + __op2: Callable[[_A], _B], + __op3: Callable[[_B], _C], + __op4: Callable[[_C], _D], ) -> _D: ... @overload def pipe( self, - op1: Callable[[Observable[_T]], _A], - op2: Callable[[_A], _B], - op3: Callable[[_B], _C], - op4: Callable[[_C], _D], - op5: Callable[[_D], _E], + __op1: Callable[[Observable[_T]], _A], + __op2: Callable[[_A], _B], + __op3: Callable[[_B], _C], + __op4: Callable[[_C], _D], + __op5: Callable[[_D], _E], ) -> _E: ... @overload def pipe( - self, # pylint: disable=function-redefined, no-self-use, too-many-arguments - op1: Callable[[Observable[_T]], _A], - op2: Callable[[_A], _B], - op3: Callable[[_B], _C], - op4: Callable[[_C], _D], - op5: Callable[[_D], _E], - op6: Callable[[_E], _F], + self, + __op1: Callable[[Observable[_T]], _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, # pylint: too-many-arguments - op1: Callable[[Observable[_T]], _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], + self, + __op1: Callable[[Observable[_T]], _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: ... @@ -371,7 +371,7 @@ def __iadd__(self, other: Observable[_T]) -> "Observable[_T]": return concat(self, other) - def __getitem__(self, key: slice) -> Observable[_T]: + def __getitem__(self, key: Union[slice, int]) -> Observable[_T]: """ Pythonic version of :func:`slice `. @@ -413,10 +413,8 @@ def __getitem__(self, key: slice) -> Observable[_T]: if isinstance(key, slice): start, stop, step = key.start, key.stop, key.step - elif isinstance(key, int): - start, stop, step = key, key + 1, 1 else: - raise TypeError("Invalid argument type.") + start, stop, step = key, key + 1, 1 from ..operators.slice import slice_ From efb4af7bc9f5f7368bc33010645369b991ce1a35 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 7 Feb 2022 09:49:23 +0100 Subject: [PATCH 037/103] Fix formatting --- rx/core/observable/generatewithrelativetime.py | 5 +++-- rx/core/operators/max.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rx/core/observable/generatewithrelativetime.py b/rx/core/observable/generatewithrelativetime.py index dd7f8d2f1..c2a8b3396 100644 --- a/rx/core/observable/generatewithrelativetime.py +++ b/rx/core/observable/generatewithrelativetime.py @@ -18,8 +18,9 @@ def generate_with_relative_time_( 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) + res = source.generate_with_relative_time( + 0, lambda x: True, lambda x: x + 1, lambda x: 0.5 + ) Args: initial_state: Initial state. diff --git a/rx/core/operators/max.py b/rx/core/operators/max.py index 7d5cbe1cb..23e71aeca 100644 --- a/rx/core/operators/max.py +++ b/rx/core/operators/max.py @@ -29,7 +29,10 @@ def max_( maximum element in the source sequence. """ - return pipe(ops.max_by(identity, comparer), ops.map(first_only)) + return pipe( + ops.max_by(identity, comparer), + ops.map(first_only), + ) __all__ = ["max_"] From 05289ed33e69bf6462428f91ec91a8107d3244d2 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:12:21 +0100 Subject: [PATCH 038/103] Fix type hints - Generate - Marbles --- rx/__init__.py | 12 ++--- rx/core/observable/amb.py | 4 +- rx/core/observable/connectableobservable.py | 4 +- rx/core/observable/forkjoin.py | 2 +- rx/core/observable/generate.py | 25 ++++++++--- rx/core/observable/marbles.py | 49 ++++++++++++--------- 6 files changed, 57 insertions(+), 39 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index f111d33f1..9f472ead5 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -58,7 +58,7 @@ def amb(*sources: Observable[_T]) -> Observable[_T]: whichever emitted the first element. """ - from .core.observable.amb import amb as amb_ + from .core.observable.amb import amb_ as amb_ return amb_(*sources) @@ -629,8 +629,10 @@ def generate_with_relative_time( def generate( - initial_state: Any, condition: typing.Predicate, iterate: typing.Mapper -) -> Observable: + initial_state: _TState, + condition: typing.Predicate[_TState], + iterate: typing.Mapper[_TState, _TState], +) -> Observable[_TState]: """Generates an observable sequence by running a state-driven loop producing the sequence's elements. @@ -652,9 +654,9 @@ def generate( Returns: The generated sequence. """ - from .core.observable.generate import _generate + from .core.observable.generate import generate_ - return _generate(initial_state, condition, iterate) + return generate_(initial_state, condition, iterate) def hot( diff --git a/rx/core/observable/amb.py b/rx/core/observable/amb.py index 602a14983..a12c31024 100644 --- a/rx/core/observable/amb.py +++ b/rx/core/observable/amb.py @@ -7,7 +7,7 @@ _T = TypeVar("_T") -def amb(*sources: Observable[_T]) -> Observable[_T]: +def amb_(*sources: Observable[_T]) -> Observable[_T]: """Propagates the observable sequence that reacts first. Example: @@ -29,4 +29,4 @@ def func(previous: Observable[_T], current: Observable[_T]): return acc -__all__ = ["amb"] +__all__ = ["amb_"] diff --git a/rx/core/observable/connectableobservable.py b/rx/core/observable/connectableobservable.py index 43ddb829e..e724f797b 100644 --- a/rx/core/observable/connectableobservable.py +++ b/rx/core/observable/connectableobservable.py @@ -33,7 +33,7 @@ def connect(self, scheduler: Optional[abc.SchedulerBase] = None): if not self.has_subscription: self.has_subscription = True - def dispose(): + def dispose() -> None: self.has_subscription = False subscription = self.source.subscribe(self.subject, scheduler=scheduler) @@ -62,7 +62,7 @@ def auto_connect(self, subscriber_count: int = 1) -> Observable[_T]: 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) diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index dcf73e7e3..1f4d46510 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -28,7 +28,7 @@ def subscribe( is_done = [False] * n has_value = [False] * n - def done(i: int): + def done(i: int) -> None: is_done[i] = True if not has_value[i]: diff --git a/rx/core/observable/generate.py b/rx/core/observable/generate.py index f27837788..ff795b3c3 100644 --- a/rx/core/observable/generate.py +++ b/rx/core/observable/generate.py @@ -1,24 +1,32 @@ -from typing import Any +from typing import Any, Optional, TypeVar, cast -from rx.core import Observable -from rx.core.typing import Mapper, Predicate +from rx.core import Observable, typing, abc from rx.disposable import MultipleAssignmentDisposable from rx.scheduler import CurrentThreadScheduler +_TState = TypeVar("_TState") -def _generate(initial_state: Any, condition: Predicate, iterate: Mapper) -> Observable: - def subscribe(observer, scheduler=None): + +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, state1=None): + def action(scheduler: abc.SchedulerBase, state1: Any = None): nonlocal first nonlocal state has_result = False - result = None + result: _TState = cast(_TState, None) try: if first: @@ -44,3 +52,6 @@ def action(scheduler, state1=None): return mad return Observable(subscribe) + + +__all__ = ["generate_"] diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index 6ed2af286..eecf9488c 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -1,11 +1,10 @@ import re import threading from datetime import datetime, timedelta -from typing import List, Mapping, Optional, Tuple +from typing import List, Mapping, Optional, Tuple, Any, Union from rx import Observable -from rx.core import notification -from rx.core.abc import SchedulerBase +from rx.core import notification, abc, typing, Notification from rx.core.typing import AbsoluteOrRelativeTime, RelativeTime from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import NewThreadScheduler @@ -37,10 +36,10 @@ def hot( string: str, timespan: RelativeTime = 0.1, duetime: AbsoluteOrRelativeTime = 0.0, - lookup: Optional[Mapping] = None, + lookup: Optional[Mapping[Union[str, float], Any]] = None, error: Optional[Exception] = None, - scheduler: Optional[SchedulerBase] = None, -) -> Observable: + scheduler: Optional[abc.SchedulerBase] = None, +) -> Observable[Any]: _scheduler = scheduler or new_thread_scheduler @@ -58,9 +57,11 @@ def hot( lock = threading.RLock() is_stopped = False - observers = [] + observers: List[abc.ObserverBase[Any]] = [] - def subscribe(observer, scheduler=None): + 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: @@ -76,8 +77,8 @@ def dispose(): return Disposable(dispose) - def create_action(notification): - def action(scheduler, state=None): + def create_action(notification: Notification[Any]) -> typing.ScheduledAction[Any]: + def action(scheduler: abc.SchedulerBase, state: Any = None): nonlocal is_stopped with lock: @@ -102,23 +103,25 @@ def action(scheduler, state=None): def from_marbles( string: str, timespan: RelativeTime = 0.1, - lookup: Optional[Mapping] = None, + lookup: Optional[Mapping[str, Any]] = None, error: Optional[Exception] = None, - scheduler: Optional[SchedulerBase] = None, -) -> Observable: + scheduler: Optional[abc.SchedulerBase] = None, +) -> Observable[Any]: messages = parse( string, timespan=timespan, lookup=lookup, error=error, raise_stopped=True ) - def subscribe(observer, scheduler_): + 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): + def schedule_msg(message: Tuple[RelativeTime, Notification[Any]]) -> None: duetime, notification = message - def action(*_, **__): + def action(scheduler: abc.SchedulerBase, state: Any = None): notification.accept(observer) disp.add(_scheduler.schedule_relative(duetime, action)) @@ -136,10 +139,10 @@ def parse( string: str, timespan: RelativeTime = 1.0, time_shift: RelativeTime = 0.0, - lookup: Optional[Mapping] = None, + lookup: Optional[Mapping[Union[str, float], Any]] = None, error: Optional[Exception] = None, raise_stopped: bool = False, -) -> List[Tuple[RelativeTime, notification.Notification]]: +) -> List[Tuple[RelativeTime, notification.Notification[Any]]]: """Convert a marble diagram string to a list of messages. Each character in the string will advance time by timespan @@ -208,7 +211,7 @@ def parse( string = string.replace(" ", "") # try to cast a string to an int, then to a float - def try_number(element): + def try_number(element: str) -> Union[float, str]: try: return int(element) except ValueError: @@ -217,7 +220,9 @@ def try_number(element): except ValueError: return element - def map_element(time, element): + def map_element( + time: typing.RelativeTime, element: str + ) -> Tuple[typing.RelativeTime, Notification[Any]]: if element == "|": return (time, notification.OnCompleted()) elif element == "#": @@ -229,7 +234,7 @@ def map_element(time, element): is_stopped = False - def check_stopped(element): + def check_stopped(element: str) -> None: nonlocal is_stopped if raise_stopped: if is_stopped: @@ -239,7 +244,7 @@ def check_stopped(element): is_stopped = True iframe = 0 - messages = [] + messages: List[Tuple[typing.RelativeTime, Notification[Any]]] = [] for results in tokens.findall(string): timestamp = iframe * timespan + time_shift From e83d6a063ced520c79c4f55df19eb3fa77f15cb9 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:12:49 +0100 Subject: [PATCH 039/103] Sort imports --- rx/core/observable/generate.py | 2 +- rx/core/observable/marbles.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rx/core/observable/generate.py b/rx/core/observable/generate.py index ff795b3c3..19ce75e87 100644 --- a/rx/core/observable/generate.py +++ b/rx/core/observable/generate.py @@ -1,6 +1,6 @@ from typing import Any, Optional, TypeVar, cast -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.disposable import MultipleAssignmentDisposable from rx.scheduler import CurrentThreadScheduler diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index eecf9488c..e6555f496 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -1,10 +1,10 @@ import re import threading from datetime import datetime, timedelta -from typing import List, Mapping, Optional, Tuple, Any, Union +from typing import Any, List, Mapping, Optional, Tuple, Union from rx import Observable -from rx.core import notification, abc, typing, Notification +from rx.core import Notification, abc, notification, typing from rx.core.typing import AbsoluteOrRelativeTime, RelativeTime from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import NewThreadScheduler From cb678bc9226027a66e9973f0c9e9080fa6a7ed2e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:17:27 +0100 Subject: [PATCH 040/103] Remove python version --- .python-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .python-version diff --git a/.python-version b/.python-version deleted file mode 100644 index 89a1ad7ad..000000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.8.12 From 250402cebd2759ccb4ee54f6994dee9d788988fa Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:18:12 +0100 Subject: [PATCH 041/103] Ignore python version for pyenv --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cef3c419a..d2b685a30 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,5 @@ _build .pyre .ionide/ + +.python-version From f5389f746c0ec1613c88902c34c3fa3fa366d55d Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:21:17 +0100 Subject: [PATCH 042/103] Use module namespacing for typing imports --- rx/core/observable/marbles.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index e6555f496..5daff1403 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -5,7 +5,6 @@ from rx import Observable from rx.core import Notification, abc, notification, typing -from rx.core.typing import AbsoluteOrRelativeTime, RelativeTime from rx.disposable import CompositeDisposable, Disposable from rx.scheduler import NewThreadScheduler @@ -34,8 +33,8 @@ def hot( string: str, - timespan: RelativeTime = 0.1, - duetime: AbsoluteOrRelativeTime = 0.0, + 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, @@ -102,8 +101,8 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): def from_marbles( string: str, - timespan: RelativeTime = 0.1, - lookup: Optional[Mapping[str, Any]] = None, + timespan: typing.RelativeTime = 0.1, + lookup: Optional[Mapping[Union[str, float], Any]] = None, error: Optional[Exception] = None, scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable[Any]: @@ -118,7 +117,9 @@ def subscribe( _scheduler = scheduler or scheduler_ or new_thread_scheduler disp = CompositeDisposable() - def schedule_msg(message: Tuple[RelativeTime, Notification[Any]]) -> None: + def schedule_msg( + message: Tuple[typing.RelativeTime, Notification[Any]] + ) -> None: duetime, notification = message def action(scheduler: abc.SchedulerBase, state: Any = None): @@ -137,12 +138,12 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): def parse( string: str, - timespan: RelativeTime = 1.0, - time_shift: RelativeTime = 0.0, + 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[RelativeTime, notification.Notification[Any]]]: +) -> 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 From bce7c6a6ab78a3794d38249cdb83763d23463600 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:42:52 +0100 Subject: [PATCH 043/103] Remove unnecessary cast --- rx/core/operators/amb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index ed783e2a5..a7376db5e 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -13,7 +13,7 @@ def amb_( ) -> Callable[[Observable[_T]], Observable[_T]]: if isinstance(right_source, Future): - obs: Observable[_T] = cast(Observable[_T], from_future(right_source)) + obs: Observable[_T] = from_future(right_source) else: obs: Observable[_T] = right_source From f896eeb334a458d61c334d0e3ff758de82df2974 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:44:17 +0100 Subject: [PATCH 044/103] Fixed typing of average ++ --- rx/core/observable/zip.py | 6 ++---- rx/core/observer/autodetachobserver.py | 2 +- rx/core/observer/observeonobserver.py | 8 +++++--- rx/core/operators/average.py | 21 ++++++++++----------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index 22c483513..b550941bf 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -7,8 +7,6 @@ from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal.concurrency import synchronized -# pylint: disable=redefined-builtin - def zip_(*args: Observable[Any]) -> Observable[Tuple[Any, ...]]: """Merges the specified observable sequences into one observable @@ -49,8 +47,8 @@ def next(i: int): 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 + # 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 diff --git a/rx/core/observer/autodetachobserver.py b/rx/core/observer/autodetachobserver.py index a4051974b..336845d76 100644 --- a/rx/core/observer/autodetachobserver.py +++ b/rx/core/observer/autodetachobserver.py @@ -11,7 +11,7 @@ class AutoDetachObserver(abc.ObserverBase[_T_in]): def __init__( self, - on_next: Optional[typing.OnNext] = None, + on_next: Optional[typing.OnNext[_T_in]] = None, on_error: Optional[typing.OnError] = None, on_completed: Optional[typing.OnCompleted] = None, ) -> None: diff --git a/rx/core/observer/observeonobserver.py b/rx/core/observer/observeonobserver.py index 41812a03f..2cdfc3b87 100644 --- a/rx/core/observer/observeonobserver.py +++ b/rx/core/observer/observeonobserver.py @@ -1,10 +1,12 @@ -from typing import Any +from typing import Any, TypeVar from .scheduledobserver import ScheduledObserver +_T = TypeVar("_T") -class ObserveOnObserver(ScheduledObserver): - def _on_next_core(self, value: Any) -> None: + +class ObserveOnObserver(ScheduledObserver[_T]): + def _on_next_core(self, value: _T) -> None: super()._on_next_core(value) self.ensure_active() diff --git a/rx/core/operators/average.py b/rx/core/operators/average.py index 6c0a1d343..5afce2315 100644 --- a/rx/core/operators/average.py +++ b/rx/core/operators/average.py @@ -1,11 +1,9 @@ -from typing import Callable, Optional, TypeVar +from typing import Any, Callable, Optional, TypeVar, cast from rx import operators -from rx.core import Observable -from rx.core.typing import Mapper +from rx.core import Observable, typing _T = TypeVar("_T") -_TKey = TypeVar("_TKey") class AverageValue(object): @@ -15,7 +13,7 @@ def __init__(self, sum: float, count: int): def average_( - key_mapper: Optional[Mapper[_T, _TKey]] = None, + key_mapper: Optional[typing.Mapper[_T, float]] = None, ) -> Callable[[Observable[_T]], Observable[float]]: def average(source: Observable[_T]) -> Observable[float]: """Partially applied average operator. @@ -35,11 +33,9 @@ def average(source: Observable[_T]) -> Observable[float]: average of the sequence of values. """ - if key_mapper: - return source.pipe( - operators.map(key_mapper), - operators.average(), - ) + key_mapper_: typing.Mapper[_T, float] = key_mapper or ( + lambda x: float(cast(Any, x)) + ) def accumulator(prev: AverageValue, cur: float) -> AverageValue: return AverageValue(sum=prev.sum + cur, count=prev.count + 1) @@ -51,11 +47,14 @@ def mapper(s: AverageValue) -> float: return s.sum / float(s.count) seed = AverageValue(sum=0, count=0) - return source.pipe( + + ret = source.pipe( + operators.map(key_mapper_), operators.scan(accumulator, seed), operators.last(), operators.map(mapper), ) + return ret return average From f266b66cfa6e11351b90ec35923ba4d13e97800c Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 08:52:39 +0100 Subject: [PATCH 045/103] Fix flaky test --- .../test_asynciothreadsafescheduler.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py b/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py index 47b36e4e9..4ef4100ba 100644 --- a/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py +++ b/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py @@ -8,12 +8,11 @@ class TestAsyncIOThreadSafeScheduler(unittest.TestCase): - def test_asyncio_threadsafe_schedule_now(self): loop = asyncio.get_event_loop() scheduler = AsyncIOThreadSafeScheduler(loop) diff = scheduler.now - datetime.utcfromtimestamp(loop.time()) - assert abs(diff) < timedelta(milliseconds=1) + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI def test_asyncio_threadsafe_schedule_now_units(self): loop = asyncio.get_event_loop() @@ -91,13 +90,10 @@ def schedule(): loop.run_until_complete(go()) def cancel_same_thread_common(self, test_body): - update_state = { - 'ran': False, - 'dispose_completed': False - } + update_state = {"ran": False, "dispose_completed": False} def action(scheduler, state): - update_state['ran'] = True + update_state["ran"] = True # Make the actual test body run in deamon thread, so that in case of # failure it doesn't hang indefinitely. @@ -116,9 +112,8 @@ async def go(): thread.daemon = True thread.start() thread.join(0.3) - assert update_state['dispose_completed'] is True - assert update_state['ran'] is False - + assert update_state["dispose_completed"] is True + assert update_state["ran"] is False def test_asyncio_threadsafe_cancel_non_relative_same_thread(self): def test_body(scheduler, action, update_state): @@ -127,11 +122,10 @@ def test_body(scheduler, action, update_state): # Test case when dispose is called on thread on which loop is not # yet running, and non-relative schedele is used. d.dispose() - update_state['dispose_completed'] = True + update_state["dispose_completed"] = True self.cancel_same_thread_common(test_body) - def test_asyncio_threadsafe_schedule_action_cancel_same_thread(self): def test_body(scheduler, action, update_state): d = scheduler.schedule_relative(0.05, action) @@ -139,18 +133,17 @@ def test_body(scheduler, action, update_state): # Test case when dispose is called on thread on which loop is not # yet running, and relative schedule is used. d.dispose() - update_state['dispose_completed'] = True + update_state["dispose_completed"] = True self.cancel_same_thread_common(test_body) - def test_asyncio_threadsafe_schedule_action_cancel_same_loop(self): def test_body(scheduler, action, update_state): d = scheduler.schedule_relative(0.1, action) def do_dispose(): d.dispose() - update_state['dispose_completed'] = True + update_state["dispose_completed"] = True # Test case when dispose is called in loop's callback. scheduler._loop.call_soon(do_dispose) From 392461142548f77ef11c53df391688f56761640d Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 15:37:10 +0100 Subject: [PATCH 046/103] Fix typing --- rx/core/abc/disposable.py | 2 +- rx/core/operators/dowhile.py | 13 +++++++-- rx/core/operators/expand.py | 55 +++++++++++++++++++++++------------- rx/core/operators/groupby.py | 4 +-- rx/operators/__init__.py | 16 ++++++----- 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/rx/core/abc/disposable.py b/rx/core/abc/disposable.py index 94b05480a..ec3bdf0d9 100644 --- a/rx/core/abc/disposable.py +++ b/rx/core/abc/disposable.py @@ -4,7 +4,7 @@ class DisposableBase(ABC): - """Disposable abstract base class. Untyped.""" + """Disposable abstract base class.""" __slots__ = () diff --git a/rx/core/operators/dowhile.py b/rx/core/operators/dowhile.py index 1bff1ad1c..d9171c80e 100644 --- a/rx/core/operators/dowhile.py +++ b/rx/core/operators/dowhile.py @@ -1,10 +1,14 @@ -from typing import Any, Callable +from typing import Callable, TypeVar from rx import operators as ops from rx.core import Observable +_T = TypeVar("_T") -def _do_while(condition: Callable[[Any], bool]) -> Callable[[Observable], Observable]: + +def do_while_( + condition: Callable[[_T], bool] +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a do while loop. @@ -17,7 +21,10 @@ def _do_while(condition: Callable[[Any], bool]) -> Callable[[Observable], Observ as the condition holds. """ - def do_while(source: Observable) -> Observable: + def do_while(source: Observable[_T]) -> Observable[_T]: return source.pipe(ops.concat(source.pipe(ops.while_do(condition)))) return do_while + + +__all__ = ["do_while_"] diff --git a/rx/core/operators/expand.py b/rx/core/operators/expand.py index a4ec4ada7..f3493f88a 100644 --- a/rx/core/operators/expand.py +++ b/rx/core/operators/expand.py @@ -1,7 +1,6 @@ -from typing import Callable +from typing import Any, Callable, List, Optional, TypeVar -from rx.core import Observable -from rx.core.typing import Mapper +from rx.core import Observable, abc, typing from rx.disposable import ( CompositeDisposable, SerialDisposable, @@ -9,9 +8,13 @@ ) from rx.scheduler import ImmediateScheduler +_T = TypeVar("_T") -def _expand(mapper: Mapper) -> Callable[[Observable], Observable]: - def expand(source: Observable) -> Observable: + +def expand_( + mapper: typing.Mapper[_T, Observable[_T]] +) -> Callable[[Observable[_T]], Observable[_T]]: + def expand(source: Observable[_T]) -> Observable[_T]: """Expands an observable sequence by recursively invoking mapper. @@ -23,32 +26,41 @@ def expand(source: Observable) -> Observable: by the recursive expansion. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: scheduler = scheduler or ImmediateScheduler.singleton() - queue = [] + queue: List[Observable[_T]] = [] m = SerialDisposable() d = CompositeDisposable(m) - active_count = [0] - is_acquired = [False] + active_count = 0 + is_acquired = False def ensure_active(): + nonlocal is_acquired + is_owner = False if queue: - is_owner = not is_acquired[0] - is_acquired[0] = True + is_owner = not is_acquired + is_acquired = True + + def action(scheduler: abc.SchedulerBase, state: Any = None): + nonlocal is_acquired, active_count - def action(scheduler, state): if queue: work = queue.pop(0) else: - is_acquired[0] = False + is_acquired = False return sad = SingleAssignmentDisposable() d.add(sad) - def on_next(value): + def on_next(value: _T) -> None: + nonlocal active_count + observer.on_next(value) result = None try: @@ -58,13 +70,15 @@ def on_next(value): return queue.append(result) - active_count[0] += 1 + active_count += 1 ensure_active() - def on_complete(): + def on_complete() -> None: + nonlocal active_count + d.remove(sad) - active_count[0] -= 1 - if active_count[0] == 0: + active_count -= 1 + if active_count == 0: observer.on_completed() sad.disposable = work.subscribe_( @@ -76,10 +90,13 @@ def on_complete(): m.disposable = scheduler.schedule(action) queue.append(source) - active_count[0] += 1 + active_count += 1 ensure_active() return d return Observable(subscribe) return expand + + +__all__ = ["expand_"] diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index 40ef08f32..f965cbd5f 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -12,7 +12,7 @@ _TValue = TypeVar("_TValue") -def _group_by( +def group_by_( key_mapper: Mapper[_T, _TKey], element_mapper: Optional[Mapper[_T, _TValue]] = None, subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, @@ -25,4 +25,4 @@ def duration_mapper(_: GroupedObservable[Any, Any]) -> Observable[Any]: ) -__all__ = ["_group_by"] +__all__ = ["group_by_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 3498b4d22..cb23ab60c 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -854,9 +854,9 @@ def do_while(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[ An observable sequence which is repeated as long as the condition holds. """ - from rx.core.operators.dowhile import _do_while + from rx.core.operators.dowhile import do_while_ - return _do_while(condition) + return do_while_(condition) def element_at(index: int) -> Callable[[Observable[_T]], Observable[_T]]: @@ -943,7 +943,9 @@ def exclusive() -> Callable[[Observable[Observable[_T]]], Observable[_T]]: return exclusive_() -def expand(mapper: Mapper) -> Callable[[Observable[_T]], Observable[_T]]: +def expand( + mapper: typing.Mapper[_T, Observable[_T]] +) -> Callable[[Observable[_T]], Observable[_T]]: """Expands an observable sequence by recursively invoking mapper. Args: @@ -955,9 +957,9 @@ def expand(mapper: Mapper) -> Callable[[Observable[_T]], Observable[_T]]: An observable sequence containing all the elements produced by the recursive expansion. """ - from rx.core.operators.expand import _expand + from rx.core.operators.expand import expand_ - return _expand(mapper) + return expand_(mapper) def filter(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: @@ -1450,9 +1452,9 @@ def group_by( corresponds to a unique key value, containing all elements that share that same key value. """ - from rx.core.operators.groupby import _group_by + from rx.core.operators.groupby import group_by_ - return _group_by(key_mapper, element_mapper, subject_mapper) + return group_by_(key_mapper, element_mapper, subject_mapper) def group_by_until( From 539b20b33f983a12618a6aa9f3b478f8f3e0a9c9 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 15:37:42 +0100 Subject: [PATCH 047/103] Remove unreachable code - `old` will never be used --- rx/disposable/singleassignmentdisposable.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/rx/disposable/singleassignmentdisposable.py b/rx/disposable/singleassignmentdisposable.py index d767b9f42..fa2e53951 100644 --- a/rx/disposable/singleassignmentdisposable.py +++ b/rx/disposable/singleassignmentdisposable.py @@ -16,7 +16,7 @@ def __init__(self) -> None: """Initializes a new instance of the SingleAssignmentDisposable class. """ - self.is_disposed = False + self.is_disposed: bool = False self.current: Optional[DisposableBase] = None self.lock = RLock() @@ -29,18 +29,12 @@ def set_disposable(self, value: DisposableBase): if self.current: raise Exception("Disposable has already been assigned") - old: Optional[DisposableBase] = None - with self.lock: should_dispose = self.is_disposed if not should_dispose: - old = self.current self.current = value - if old is not None: - old.dispose() - - if should_dispose and value: + if self.is_disposed and value: value.dispose() disposable = property(get_disposable, set_disposable) From 12aaa3d43aa7e6d955e76a467ab1ebe2de385c4e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 22:44:11 +0100 Subject: [PATCH 048/103] Scheduler fixes + cleanup --- rx/scheduler/eventloop/__init__.py | 9 +++++++++ rx/scheduler/mainloop/__init__.py | 8 ++++++++ rx/scheduler/mainloop/gtkscheduler.py | 6 +++--- rx/scheduler/mainloop/qtscheduler.py | 2 +- rx/scheduler/mainloop/wxscheduler.py | 6 +++--- rx/subject/asyncsubject.py | 4 ++-- rx/subject/behaviorsubject.py | 2 +- 7 files changed, 27 insertions(+), 10 deletions(-) diff --git a/rx/scheduler/eventloop/__init__.py b/rx/scheduler/eventloop/__init__.py index 016b9b750..6a11e13a3 100644 --- a/rx/scheduler/eventloop/__init__.py +++ b/rx/scheduler/eventloop/__init__.py @@ -4,3 +4,12 @@ from .geventscheduler import GEventScheduler from .ioloopscheduler import IOLoopScheduler from .twistedscheduler import TwistedScheduler + +__all__ = [ + "AsyncIOScheduler", + "AsyncIOThreadSafeScheduler", + "EventletScheduler", + "GEventScheduler", + "IOLoopScheduler", + "TwistedScheduler", +] diff --git a/rx/scheduler/mainloop/__init__.py b/rx/scheduler/mainloop/__init__.py index 768c0bbd1..3e1d0a3af 100644 --- a/rx/scheduler/mainloop/__init__.py +++ b/rx/scheduler/mainloop/__init__.py @@ -3,3 +3,11 @@ from .qtscheduler import QtScheduler from .tkinterscheduler import TkinterScheduler from .wxscheduler import WxScheduler + +__all__ = [ + "GtkScheduler", + "PyGameScheduler", + "QtScheduler", + "TkinterScheduler", + "WxScheduler", +] diff --git a/rx/scheduler/mainloop/gtkscheduler.py b/rx/scheduler/mainloop/gtkscheduler.py index 1928230f2..87ca4bc9b 100644 --- a/rx/scheduler/mainloop/gtkscheduler.py +++ b/rx/scheduler/mainloop/gtkscheduler.py @@ -29,7 +29,7 @@ def __init__(self, glib: Any) -> None: def _gtk_schedule( self, time: typing.AbsoluteOrRelativeTime, - action: typing.ScheduledSingleOrPeriodicAction, + action: typing.ScheduledSingleOrPeriodicAction[_TState], state: Optional[_TState] = None, periodic: bool = False, ) -> abc.DisposableBase: @@ -46,7 +46,7 @@ def timer_handler(_) -> bool: nonlocal state if periodic: - state = cast(typing.ScheduledPeriodicAction, action)(state) + state = cast(typing.ScheduledPeriodicAction[_TState], action)(state) else: sad.disposable = self.invoke_action( cast(typing.ScheduledAction[_TState], action), state=state @@ -120,7 +120,7 @@ def schedule_absolute( def schedule_periodic( self, period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, + action: typing.ScheduledPeriodicAction[_TState], state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. diff --git a/rx/scheduler/mainloop/qtscheduler.py b/rx/scheduler/mainloop/qtscheduler.py index 31bd5062b..88ad3a668 100644 --- a/rx/scheduler/mainloop/qtscheduler.py +++ b/rx/scheduler/mainloop/qtscheduler.py @@ -102,7 +102,7 @@ def schedule_absolute( def schedule_periodic( self, period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, + action: typing.ScheduledPeriodicAction[_TState], state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. diff --git a/rx/scheduler/mainloop/wxscheduler.py b/rx/scheduler/mainloop/wxscheduler.py index 6e1e275f0..428955a5f 100644 --- a/rx/scheduler/mainloop/wxscheduler.py +++ b/rx/scheduler/mainloop/wxscheduler.py @@ -49,7 +49,7 @@ def cancel_all(self) -> None: def _wxtimer_schedule( self, time: typing.AbsoluteOrRelativeTime, - action: typing.ScheduledSingleOrPeriodicAction, + action: typing.ScheduledSingleOrPeriodicAction[_TState], state: Optional[_TState] = None, periodic: bool = False, ) -> abc.DisposableBase: @@ -60,7 +60,7 @@ def _wxtimer_schedule( def interval() -> None: nonlocal state if periodic: - state = cast(typing.ScheduledPeriodicAction, action)(state) + state = cast(typing.ScheduledPeriodicAction[_TState], action)(state) else: sad.disposable = cast(typing.ScheduledAction[_TState], action)( scheduler, state @@ -141,7 +141,7 @@ def schedule_absolute( def schedule_periodic( self, period: typing.RelativeTime, - action: typing.ScheduledPeriodicAction, + action: typing.ScheduledPeriodicAction[_TState], state: Optional[_TState] = None, ) -> abc.DisposableBase: """Schedules a periodic piece of work to be executed in the loop. diff --git a/rx/subject/asyncsubject.py b/rx/subject/asyncsubject.py index 5569a824d..3989ccada 100644 --- a/rx/subject/asyncsubject.py +++ b/rx/subject/asyncsubject.py @@ -1,6 +1,6 @@ -from typing import Any, Optional, TypeVar, cast +from typing import Optional, TypeVar, cast -from rx.core import abc, typing +from rx.core import abc from rx.disposable import Disposable from .innersubscription import InnerSubscription diff --git a/rx/subject/behaviorsubject.py b/rx/subject/behaviorsubject.py index ad66c5397..17de74668 100644 --- a/rx/subject/behaviorsubject.py +++ b/rx/subject/behaviorsubject.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, TypeVar +from typing import Optional, TypeVar from rx.core import abc from rx.disposable import Disposable From 66ac204e0abc6741bc97d3b045c7a83ec5dd84c5 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 22:44:34 +0100 Subject: [PATCH 049/103] Groupby fixes --- rx/core/operators/groupby.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index f965cbd5f..401ba2517 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -1,23 +1,25 @@ from typing import Any, Callable, Optional, TypeVar -import rx -from rx import operators as ops -from rx.core import Observable -from rx.core.observable.groupedobservable import GroupedObservable -from rx.core.typing import Mapper +from rx.core import Observable, GroupedObservable, typing from rx.subject import Subject _T = TypeVar("_T") _TKey = TypeVar("_TKey") _TValue = TypeVar("_TValue") +# pylint: disable=import-outside-toplevel + def group_by_( - key_mapper: Mapper[_T, _TKey], - element_mapper: Optional[Mapper[_T, _TValue]] = None, + key_mapper: typing.Mapper[_T, _TKey], + element_mapper: Optional[typing.Mapper[_T, _TValue]] = None, subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, ) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: + from rx import operators as ops + def duration_mapper(_: GroupedObservable[Any, Any]) -> Observable[Any]: + import rx + return rx.never() return ops.group_by_until( From 005ac1aff3eca046a4c15166494509a96eca7055 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 22:48:48 +0100 Subject: [PATCH 050/103] Fix import sorting --- rx/core/operators/groupby.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rx/core/operators/groupby.py b/rx/core/operators/groupby.py index 401ba2517..dfabc488f 100644 --- a/rx/core/operators/groupby.py +++ b/rx/core/operators/groupby.py @@ -1,6 +1,6 @@ from typing import Any, Callable, Optional, TypeVar -from rx.core import Observable, GroupedObservable, typing +from rx.core import GroupedObservable, Observable, typing from rx.subject import Subject _T = TypeVar("_T") From 74744fd36b58760406eeb6fa76e3d958858ff112 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 23:31:25 +0100 Subject: [PATCH 051/103] Fixed more typing issues --- rx/core/operators/delaysubscription.py | 17 ++++++---- rx/core/operators/delaywithmapper.py | 35 ++++++++++++++------ rx/core/operators/dematerialize.py | 4 +-- rx/core/operators/distinct.py | 46 ++++++++++++++++---------- rx/operators/__init__.py | 35 ++++++++++++-------- 5 files changed, 87 insertions(+), 50 deletions(-) diff --git a/rx/core/operators/delaysubscription.py b/rx/core/operators/delaysubscription.py index 2adcc400b..89b12c0dd 100644 --- a/rx/core/operators/delaysubscription.py +++ b/rx/core/operators/delaysubscription.py @@ -1,15 +1,17 @@ -from typing import Callable, Optional +from typing import Any, Callable, Optional, TypeVar import rx from rx import operators as ops -from rx.core import Observable, typing +from rx.core import Observable, typing, abc +_T = TypeVar("_T") -def _delay_subscription( + +def delay_subscription_( duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: - def delay_subscription(source: Observable) -> Observable: +) -> Callable[[Observable[_T]], Observable[_T]]: + def delay_subscription(source: Observable[_T]) -> Observable[_T]: """Time shifts the observable sequence by delaying the subscription. Exampeles. @@ -22,7 +24,7 @@ def delay_subscription(source: Observable) -> Observable: Time-shifted sequence. """ - def mapper(_) -> Observable: + def mapper(_: Any) -> Observable[_T]: return rx.empty() return source.pipe( @@ -30,3 +32,6 @@ def mapper(_) -> Observable: ) return delay_subscription + + +__all__ = ["delay_subscription_"] diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index d988a6c4f..8a5c9a110 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -1,4 +1,4 @@ -from typing import Callable +from typing import Any, Callable, Optional, TypeVar, Union from rx.core import Observable, abc, typing from rx.disposable import ( @@ -7,11 +7,18 @@ SingleAssignmentDisposable, ) +_T = TypeVar("_T") -def _delay_with_mapper( - subscription_delay=None, delay_duration_mapper=None -) -> Callable[[Observable], Observable]: - def delay_with_mapper(source: Observable) -> Observable: + +def delay_with_mapper_( + subscription_delay: Union[ + Observable[Any], + typing.Mapper[Any, Observable[Any]], + None, + ] = None, + delay_duration_mapper: Optional[typing.Mapper[_T, Observable[Any]]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: + def delay_with_mapper(source: Observable[_T]) -> Observable[_T]: """Time shifts the observable sequence based on a subscription delay and a delay mapper function for each element. @@ -37,7 +44,10 @@ def delay_with_mapper(source: Observable) -> Observable: else: mapper = subscription_delay - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: delays = CompositeDisposable() at_end = [False] @@ -48,22 +58,22 @@ def done(): subscription = SerialDisposable() def start(): - def on_next(x): + def on_next(x: _T) -> None: try: delay = mapper(x) - except Exception as error: + except Exception as error: # pylint: disable=broad-except observer.on_error(error) return d = SingleAssignmentDisposable() delays.add(d) - def on_next(_): + def on_next(_: Any) -> None: observer.on_next(x) delays.remove(d) done() - def on_completed(): + def on_completed() -> None: observer.on_next(x) delays.remove(d) done() @@ -72,7 +82,7 @@ def on_completed(): on_next, observer.on_error, on_completed, scheduler ) - def on_completed(): + def on_completed() -> None: at_end[0] = True subscription.dispose() done() @@ -93,3 +103,6 @@ def on_completed(): return Observable(subscribe) return delay_with_mapper + + +__all__ = ["delay_with_mapper_"] diff --git a/rx/core/operators/dematerialize.py b/rx/core/operators/dematerialize.py index 7e1bf27db..4e9bef990 100644 --- a/rx/core/operators/dematerialize.py +++ b/rx/core/operators/dematerialize.py @@ -5,7 +5,7 @@ _T = TypeVar("_T") -def _dematerialize() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: +def dematerialize_() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: def dematerialize(source: Observable[Notification[_T]]) -> Observable[_T]: """Partially applied dematerialize operator. @@ -33,4 +33,4 @@ def on_next(value: Notification[_T]) -> None: return dematerialize -__all__ = ["_dematerialize"] +__all__ = ["dematerialize_"] diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index aa08fcb38..92e53e3c5 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -1,39 +1,45 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, cast, Generic, List -from rx.core import Observable -from rx.core.typing import Comparer, Mapper +from rx.core import Observable, typing, abc from rx.internal.basic import default_comparer +_T = TypeVar("_T") +_TKey = TypeVar("_TKey") -def array_index_of_comparer(array, item, comparer): + +def array_index_of_comparer( + array: List[_TKey], item: _TKey, comparer: typing.Comparer[_TKey] +): for i, a in enumerate(array): if comparer(a, item): return i return -1 -class HashSet: - def __init__(self, comparer): +class HashSet(Generic[_TKey]): + def __init__(self, comparer: typing.Comparer[_TKey]): self.comparer = comparer - self.set = [] + self.set: List[_TKey] = [] - def push(self, value): + def push(self, value: _TKey): ret_value = array_index_of_comparer(self.set, value, self.comparer) == -1 if ret_value: self.set.append(value) return ret_value -def _distinct( - key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: +def distinct_( + key_mapper: Optional[typing.Mapper[_T, _TKey]] = None, + comparer: Optional[typing.Comparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: comparer = comparer or default_comparer - def distinct(source: Observable) -> Observable: + def distinct(source: Observable[_T]) -> Observable[_T]: """Returns an observable sequence that contains only distinct elements according to the key_mapper and the comparer. Usage of - this operator should be considered carefully due to the maintenance - of an internal lookup structure which can grow large. + this operator should be considered carefully due to the + maintenance of an internal lookup structure which can grow + large. Examples: >>> res = obs = distinct(source) @@ -47,11 +53,14 @@ def distinct(source: Observable) -> Observable: sequence. """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: hashset = HashSet(comparer) - def on_next(x): - key = x + def on_next(x: _T) -> None: + key = cast(_TKey, x) if key_mapper: try: @@ -69,3 +78,6 @@ def on_next(x): return Observable(subscribe) return distinct + + +__all__ = ["distinct_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index cb23ab60c..1d836a71a 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1,4 +1,5 @@ -# pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin +# pylint: disable=too-many-lines,redefined-outer-name,redefined-builtin,import-outside-toplevel + from asyncio import Future from typing import ( @@ -578,7 +579,7 @@ def default_if_empty( def delay_subscription( duetime: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[_T]]: """Time shifts the observable sequence by delaying the subscription. @@ -601,14 +602,19 @@ def delay_subscription( A function that take a source observable and returns a time-shifted observable sequence. """ - from rx.core.operators.delaysubscription import _delay_subscription + from rx.core.operators.delaysubscription import delay_subscription_ - return _delay_subscription(duetime, scheduler=scheduler) + return delay_subscription_(duetime, scheduler=scheduler) def delay_with_mapper( - subscription_delay=None, delay_duration_mapper=None -) -> Callable[[Observable], Observable]: + subscription_delay: Union[ + Observable[Any], + typing.Mapper[Any, Observable[Any]], + None, + ] = None, + delay_duration_mapper: Optional[typing.Mapper[_T, Observable[Any]]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Time shifts the observable sequence based on a subscription delay and a delay mapper function for each element. @@ -635,9 +641,9 @@ def delay_with_mapper( A function that takes an observable source and returns a time-shifted observable sequence. """ - from rx.core.operators.delaywithmapper import _delay_with_mapper + from rx.core.operators.delaywithmapper import delay_with_mapper_ - return _delay_with_mapper(subscription_delay, delay_duration_mapper) + return delay_with_mapper_(subscription_delay, delay_duration_mapper) def dematerialize() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: @@ -650,9 +656,9 @@ def dematerialize() -> Callable[[Observable[Notification[_T]]], Observable[_T]]: An observable sequence exhibiting the behavior corresponding to the source sequence's notification values. """ - from rx.core.operators.dematerialize import _dematerialize + from rx.core.operators.dematerialize import dematerialize_ - return _dematerialize() + return dematerialize_() def delay( @@ -690,8 +696,9 @@ def delay( def distinct( - key_mapper: Optional[Mapper] = None, comparer: Optional[Comparer] = None -) -> Callable[[Observable], Observable]: + key_mapper: Optional[Mapper[_T, _TKey]] = None, + comparer: Optional[Comparer[_TKey]] = None, +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns an observable sequence that contains only distinct elements according to the key_mapper and the comparer. Usage of this operator should be considered carefully due to the maintenance @@ -721,9 +728,9 @@ def distinct( elements, based on a computed key value, from the source sequence. """ - from rx.core.operators.distinct import _distinct + from rx.core.operators.distinct import distinct_ - return _distinct(key_mapper, comparer) + return distinct_(key_mapper, comparer) def distinct_until_changed( From 6e5fe0168d4598635ae67b7b202a6c80c4369b3f Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 9 Feb 2022 23:43:29 +0100 Subject: [PATCH 052/103] Typing fixes --- rx/core/operators/delaywithmapper.py | 5 +++-- rx/testing/testscheduler.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index 8a5c9a110..50a06a3a1 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -35,8 +35,8 @@ def delay_with_mapper(source: Observable[_T]) -> Observable[_T]: Returns: Time-shifted observable sequence. """ - - sub_delay, mapper = None, None + sub_delay: Optional[Observable[Any]] = None + mapper: Optional[typing.Mapper[Any, Observable[Any]]] = None if isinstance(subscription_delay, abc.ObservableBase): mapper = delay_duration_mapper @@ -60,6 +60,7 @@ def done(): def start(): def on_next(x: _T) -> None: try: + assert mapper delay = mapper(x) except Exception as error: # pylint: disable=broad-except observer.on_error(error) diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index dd8c97b0b..cb7b88ad8 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -135,7 +135,7 @@ def create_hot_observable( def create_cold_observable( self, *args: Union[Recorded[_T], List[Recorded[_T]]] - ) -> Observable[_T]: + ) -> ColdObservable[_T]: """Creates a cold observable using the specified timestamped notification messages either as an array or arguments. From d7db9bc841c5d0f927175a7b67181761e7b95784 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 07:18:08 +0100 Subject: [PATCH 053/103] Typing fixes for sequence_equal --- rx/core/operators/sequenceequal.py | 5 +++-- rx/operators/__init__.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index 3bc6d437a..60de61328 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -1,4 +1,4 @@ -from typing import Callable, Iterable, List, Optional, TypeVar +from typing import Callable, Iterable, List, Optional, TypeVar, Union import rx from rx.core import Observable, abc, typing @@ -9,7 +9,8 @@ def sequence_equal_( - second: Observable[_T], comparer: Optional[typing.Comparer[_T]] = None + second: Union[Observable[_T], Iterable[_T]], + comparer: Optional[typing.Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[bool]]: comparer = comparer or default_comparer if isinstance(second, Iterable): diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 1d836a71a..8f28925f9 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2474,7 +2474,7 @@ def scan( def sequence_equal( - second: Observable[_T], comparer: Optional[Comparer[_T]] = None + second: Union[Observable[_T], Iterable[_T]], comparer: Optional[Comparer[_T]] = None ) -> Callable[[Observable[_T]], Observable[bool]]: """Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer. @@ -2494,7 +2494,7 @@ def sequence_equal( >>> res = sequence_equal(rx.return_value({ "value": 42 }), lambda x, y: x.value == y.value) Args: - second: Second observable sequence or array to compare. + second: Second observable sequence or iterable to compare. comparer: [Optional] Comparer used to compare elements of both sequences. No guarantees on order of comparer arguments. From e0f9955ccf42105a17bf717784eb18f45e7a91ce Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 07:21:02 +0100 Subject: [PATCH 054/103] Fix bool return type for composite disposable --- rx/disposable/booleandisposable.py | 2 +- rx/disposable/compositedisposable.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rx/disposable/booleandisposable.py b/rx/disposable/booleandisposable.py index 02bf4a7f8..c49622024 100644 --- a/rx/disposable/booleandisposable.py +++ b/rx/disposable/booleandisposable.py @@ -14,7 +14,7 @@ def __init__(self): super().__init__() - def dispose(self): + def dispose(self) -> None: """Sets the status to disposed""" self.is_disposed = True diff --git a/rx/disposable/compositedisposable.py b/rx/disposable/compositedisposable.py index b0b1cb8ac..995c5ed88 100644 --- a/rx/disposable/compositedisposable.py +++ b/rx/disposable/compositedisposable.py @@ -18,7 +18,7 @@ def __init__(self, *args: Any): self.lock = RLock() super(CompositeDisposable, self).__init__() - def add(self, item: abc.DisposableBase): + def add(self, item: abc.DisposableBase) -> None: """Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed @@ -35,12 +35,12 @@ def add(self, item: abc.DisposableBase): if should_dispose: item.dispose() - def remove(self, item: abc.DisposableBase): + def remove(self, item: abc.DisposableBase) -> bool: """Removes and disposes the first occurrence of a disposable from the CompositeDisposable.""" if self.is_disposed: - return + return False should_dispose = False with self.lock: From 12ad2b6207614679337a43ad237e3bc0a5a8cd1d Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 07:24:19 +0100 Subject: [PATCH 055/103] Fixed import sorting --- rx/core/operators/delaysubscription.py | 2 +- rx/core/operators/distinct.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rx/core/operators/delaysubscription.py b/rx/core/operators/delaysubscription.py index 89b12c0dd..f9f080f5f 100644 --- a/rx/core/operators/delaysubscription.py +++ b/rx/core/operators/delaysubscription.py @@ -2,7 +2,7 @@ import rx from rx import operators as ops -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing _T = TypeVar("_T") diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index 92e53e3c5..d08d0329d 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -1,6 +1,6 @@ -from typing import Callable, Optional, TypeVar, cast, Generic, List +from typing import Callable, Generic, List, Optional, TypeVar, cast -from rx.core import Observable, typing, abc +from rx.core import Observable, abc, typing from rx.internal.basic import default_comparer _T = TypeVar("_T") From 7863d0a14d6b6c4011b3b642d7c16ebc91bb25f4 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 07:57:57 +0100 Subject: [PATCH 056/103] Type hints for examples (partial) --- examples/autocomplete/autocomplete_asyncio.py | 56 ++++++++++--------- examples/konamicode/konamicode.py | 25 +++++---- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/examples/autocomplete/autocomplete_asyncio.py b/examples/autocomplete/autocomplete_asyncio.py index 0f48c320b..f6a9e7b07 100644 --- a/examples/autocomplete/autocomplete_asyncio.py +++ b/examples/autocomplete/autocomplete_asyncio.py @@ -9,11 +9,13 @@ import os import asyncio +from asyncio import Future +from typing import Dict, Union from tornado.platform.asyncio import AsyncIOMainLoop from tornado.escape import json_decode from tornado.httputil import url_concat -from tornado.httpclient import AsyncHTTPClient +from tornado.httpclient import AsyncHTTPClient, HTTPResponse from tornado.web import RequestHandler, StaticFileHandler, Application, url from tornado.websocket import WebSocketHandler @@ -22,22 +24,20 @@ from rx.subject import Subject -def search_wikipedia(term): +def search_wikipedia(term: str) -> Future[HTTPResponse]: """Search Wikipedia for a given term""" - url = 'http://en.wikipedia.org/w/api.php' + url = "http://en.wikipedia.org/w/api.php" - params = { - "action": 'opensearch', - "search": term, - "format": 'json' - } + params = {"action": "opensearch", "search": term, "format": "json"} # Must set a user agent for non-browser requests to Wikipedia - user_agent = "RxPY/1.0 (https://github.com/dbrattli/RxPY; dag@brattli.net) Tornado/4.0.1" + user_agent = ( + "RxPY/1.0 (https://github.com/dbrattli/RxPY; dag@brattli.net) Tornado/4.0.1" + ) url = url_concat(url, params) http_client = AsyncHTTPClient() - return http_client.fetch(url, method='GET', user_agent=user_agent) + return http_client.fetch(url, method="GET", user_agent=user_agent) class WSHandler(WebSocketHandler): @@ -48,26 +48,30 @@ def open(self): # A Subject is both an observable and observer, so we can both subscribe # to it and also feed (send) it with new values - self.subject = Subject() + self.subject: Subject[Dict[str, str]] = Subject() # Get all distinct key up events from the input and only fire if long enough and distinct searcher = self.subject.pipe( ops.map(lambda x: x["term"]), - ops.filter(lambda text: len(text) > 2), # Only if the text is longer than 2 characters - ops.debounce(0.750), # Pause for 750ms - ops.distinct_until_changed(), # Only if the value has changed - ops.flat_map_latest(search_wikipedia) + ops.filter( + lambda text: len(text) > 2 + ), # Only if the text is longer than 2 characters + ops.debounce(0.750), # Pause for 750ms + ops.distinct_until_changed(), # Only if the value has changed + ops.flat_map_latest(search_wikipedia), ) - def send_response(x): + def send_response(x: HTTPResponse) -> None: self.write_message(x.body) - def on_error(ex): + def on_error(ex: Exception): print(ex) - searcher.subscribe(send_response, on_error, scheduler=scheduler) + searcher.subscribe_( + on_next=send_response, on_error=on_error, scheduler=scheduler + ) - def on_message(self, message): + def on_message(self, message: Union[bytes, str]): obj = json_decode(message) self.subject.on_next(obj) @@ -84,16 +88,18 @@ def main(): AsyncIOMainLoop().make_current() port = os.environ.get("PORT", 8080) - app = Application([ - url(r"/", MainHandler), - (r'/ws', WSHandler), - (r'/static/(.*)', StaticFileHandler, {'path': "."}) - ]) + app = Application( + [ + url(r"/", MainHandler), + (r"/ws", WSHandler), + (r"/static/(.*)", StaticFileHandler, {"path": "."}), + ] + ) print("Starting server at port: %s" % port) app.listen(port) asyncio.get_event_loop().run_forever() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/konamicode/konamicode.py b/examples/konamicode/konamicode.py index a19da403d..a5c68bcdc 100644 --- a/examples/konamicode/konamicode.py +++ b/examples/konamicode/konamicode.py @@ -1,4 +1,5 @@ import os +from typing import Dict, Union from tornado.websocket import WebSocketHandler from tornado.web import RequestHandler, StaticFileHandler, Application, url @@ -18,7 +19,7 @@ def open(self): # A Subject is both an observable and observer, so we can both subscribe # to it and also feed (on_next) it with new values - self.subject = Subject() + self.subject: Subject[Dict[str, int]] = Subject() # Now we take on our magic glasses and project the stream of bytes into # a ... @@ -30,12 +31,12 @@ def open(self): # 3. stream of booleans, True or False ops.flat_map(lambda win: win.pipe(ops.sequence_equal(codes))), # 4. stream of Trues - ops.filter(lambda equal: equal) + ops.filter(lambda equal: equal), ) # 4. we then subscribe to the Trues, and signal Konami! if we see any - query.subscribe(lambda x: self.write_message("Konami!")) + query.subscribe_(on_next=lambda x: self.write_message("Konami!")) - def on_message(self, message): + def on_message(self, message: Union[str, bytes]): obj = json_decode(message) self.subject.on_next(obj) @@ -50,15 +51,17 @@ def get(self): def main(): port = os.environ.get("PORT", 8080) - app = Application([ - url(r"/", MainHandler), - (r'/ws', WSHandler), - (r'/static/(.*)', StaticFileHandler, {'path': "."}) - ]) + app = Application( + [ + url(r"/", MainHandler), + (r"/ws", WSHandler), + (r"/static/(.*)", StaticFileHandler, {"path": "."}), + ] + ) print("Starting server at port: %s" % port) - app.listen(port) + app.listen(int(port)) ioloop.IOLoop.current().start() -if __name__ == '__main__': +if __name__ == "__main__": main() From 6b177e77d6d4d807f726b20a87ad7c0e4a79787b Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 07:58:18 +0100 Subject: [PATCH 057/103] Star map overloads --- rx/operators/__init__.py | 55 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 8f28925f9..7604100e9 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -47,6 +47,11 @@ _TState = TypeVar("_TState") _TValue = TypeVar("_TValue") +_A = TypeVar("_A") +_B = TypeVar("_B") +_C = TypeVar("_C") +_D = TypeVar("_D") + def all(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: """Determines whether all elements of an observable sequence satisfy @@ -2943,9 +2948,30 @@ def some( return some_(predicate) +@overload def starmap( - mapper: Optional[Callable[..., _T]] = None -) -> Callable[[Observable[Tuple[Any, ...]]], Observable[_T]]: + mapper: Optional[Callable[[_A, _B], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B]]], Observable[_T]]: + ... + + +@overload +def starmap( + mapper: Optional[Callable[[_A, _B, _C], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: + ... + + +@overload +def starmap( + mapper: Optional[Callable[[_A, _B, _C, _D], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B, _C, _D]]], Observable[_T]]: + ... + + +def starmap( + mapper: Optional[Callable[..., Any]] = None +) -> Callable[[Observable[Tuple[Any, ...]]], Observable[Any]]: """The starmap operator. Unpack arguments grouped as tuple elements of an observable @@ -2980,7 +3006,28 @@ def starmap( if mapper is None: return pipe(identity) - return pipe(map(lambda values: cast(Mapper, mapper)(*values))) + return pipe(map(lambda values: mapper(*values))) + + +@overload +def starmap_indexed( + mapper: Optional[Callable[[_A, _B, int], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B]]], Observable[_T]]: + ... + + +@overload +def starmap_indexed( + mapper: Optional[Callable[[_A, _B, _C, int], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: + ... + + +@overload +def starmap_indexed( + mapper: Optional[Callable[[_A, _B, _C, _D, int], _T]] = None +) -> Callable[[Observable[Tuple[_A, _B, _C, _D]]], Observable[_T]]: + ... def starmap_indexed( @@ -3012,7 +3059,7 @@ def starmap_indexed( if mapper is None: return pipe(identity) - return pipe(map(lambda values: cast(MapperIndexed[Any, Any], mapper)(*values))) + return pipe(map(lambda values: mapper(*values))) def start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: From 8ac47d09369040e974664ea030c781fcd256891c Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 17:29:18 +0100 Subject: [PATCH 058/103] Refactor subscribe logic - So Observable subscribe is compatible with abc.ObservableBase subscribe --- examples/autocomplete/autocomplete.py | 2 +- examples/autocomplete/autocomplete_asyncio.py | 2 +- examples/konamicode/konamicode.py | 2 +- .../Part VII - Meta Operations.ipynb | 8 +-- rx/core/abc/observable.py | 8 ++- rx/core/observable/catch.py | 7 +- rx/core/observable/combinelatest.py | 4 +- rx/core/observable/concat.py | 7 +- rx/core/observable/forkjoin.py | 4 +- rx/core/observable/fromfuture.py | 1 - rx/core/observable/observable.py | 70 +++---------------- rx/core/observable/onerrorresumenext.py | 4 +- rx/core/observable/withlatestfrom.py | 14 ++-- rx/core/observable/zip.py | 4 +- rx/core/operators/amb.py | 8 +-- rx/core/operators/catch.py | 39 +++++++---- rx/core/operators/debounce.py | 6 +- rx/core/operators/defaultifempty.py | 4 +- rx/core/operators/delay.py | 2 +- rx/core/operators/delaywithmapper.py | 10 +-- rx/core/operators/dematerialize.py | 4 +- rx/core/operators/distinct.py | 4 +- rx/core/operators/distinctuntilchanged.py | 2 +- rx/core/operators/do.py | 32 ++++++--- rx/core/operators/elementatordefault.py | 4 +- rx/core/operators/exclusive.py | 6 +- rx/core/operators/expand.py | 4 +- rx/core/operators/filter.py | 8 +-- rx/core/operators/find.py | 4 +- rx/core/operators/firstordefault.py | 4 +- rx/core/operators/groupbyuntil.py | 6 +- rx/core/operators/groupjoin.py | 17 +++-- rx/core/operators/ignoreelements.py | 4 +- rx/core/operators/join.py | 22 +++--- rx/core/operators/lastordefault.py | 4 +- rx/core/operators/map.py | 8 ++- rx/core/operators/materialize.py | 4 +- rx/core/operators/merge.py | 16 +++-- rx/core/operators/minby.py | 4 +- rx/core/operators/pairwise.py | 2 +- rx/core/operators/sample.py | 11 ++- rx/core/operators/sequenceequal.py | 8 +-- rx/core/operators/singleordefault.py | 4 +- rx/core/operators/skip.py | 4 +- rx/core/operators/skiplast.py | 4 +- rx/core/operators/skiplastwithtime.py | 4 +- rx/core/operators/skipuntil.py | 8 +-- rx/core/operators/skipuntilwithtime.py | 4 +- rx/core/operators/skipwhile.py | 4 +- rx/core/operators/skipwithtime.py | 4 +- rx/core/operators/some.py | 4 +- rx/core/operators/switchlatest.py | 4 +- rx/core/operators/take.py | 4 +- rx/core/operators/takelast.py | 4 +- rx/core/operators/takelastbuffer.py | 4 +- rx/core/operators/takelastwithtime.py | 4 +- rx/core/operators/takeuntil.py | 4 +- rx/core/operators/takewhile.py | 8 +-- rx/core/operators/throttlefirst.py | 2 +- rx/core/operators/timeout.py | 4 +- rx/core/operators/timeoutwithmapper.py | 8 +-- rx/core/operators/todict.py | 4 +- rx/core/operators/tofuture.py | 2 +- rx/core/operators/toiterable.py | 4 +- rx/core/operators/tomarbles.py | 2 +- rx/core/operators/toset.py | 4 +- rx/core/operators/window.py | 35 ++++++---- rx/core/operators/windowwithcount.py | 4 +- rx/core/operators/windowwithtime.py | 2 +- rx/core/operators/windowwithtimeorcount.py | 2 +- rx/core/operators/zip.py | 4 +- rx/core/run.py | 12 ++-- rx/operators/__init__.py | 33 +++++---- .../test_connectableobservable.py | 46 ++++++++++-- tests/test_observable/test_fromiterable.py | 17 +++-- tests/test_observable/test_observeon.py | 22 +++--- tests/test_observable/test_publish.py | 26 +++++-- tests/test_observable/test_subscribeon.py | 17 ++--- 78 files changed, 391 insertions(+), 315 deletions(-) diff --git a/examples/autocomplete/autocomplete.py b/examples/autocomplete/autocomplete.py index b6e69d1ff..0680ad960 100644 --- a/examples/autocomplete/autocomplete.py +++ b/examples/autocomplete/autocomplete.py @@ -65,7 +65,7 @@ def send_response(x: HTTPResponse) -> None: def on_error(ex: Exception) -> None: print(ex) - searcher.subscribe_( + searcher.subscribe( on_next=send_response, on_error=on_error, scheduler=scheduler ) diff --git a/examples/autocomplete/autocomplete_asyncio.py b/examples/autocomplete/autocomplete_asyncio.py index f6a9e7b07..cedbc58f3 100644 --- a/examples/autocomplete/autocomplete_asyncio.py +++ b/examples/autocomplete/autocomplete_asyncio.py @@ -67,7 +67,7 @@ def send_response(x: HTTPResponse) -> None: def on_error(ex: Exception): print(ex) - searcher.subscribe_( + searcher.subscribe( on_next=send_response, on_error=on_error, scheduler=scheduler ) diff --git a/examples/konamicode/konamicode.py b/examples/konamicode/konamicode.py index a5c68bcdc..d7a27a750 100644 --- a/examples/konamicode/konamicode.py +++ b/examples/konamicode/konamicode.py @@ -34,7 +34,7 @@ def open(self): ops.filter(lambda equal: equal), ) # 4. we then subscribe to the Trues, and signal Konami! if we see any - query.subscribe_(on_next=lambda x: self.write_message("Konami!")) + query.subscribe(on_next=lambda x: self.write_message("Konami!")) def on_message(self, message: Union[str, bytes]): obj = json_decode(message) diff --git a/notebooks/reactivex.io/Part VII - Meta Operations.ipynb b/notebooks/reactivex.io/Part VII - Meta Operations.ipynb index 2ac768fa5..b5da6226a 100644 --- a/notebooks/reactivex.io/Part VII - Meta Operations.ipynb +++ b/notebooks/reactivex.io/Part VII - Meta Operations.ipynb @@ -421,7 +421,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# I want an operator to operate on a particular Scheduler: **[subscribe_on](http://reactivex.io/documentation/operators/subscribeon.html)**\n", + "# I want an operator to operate on a particular Scheduler: **[subscribe_on](http://reactivex.io/documentation/operators/subscribe_on.html)**\n", "\n", "Advanced feature: Adding **side effects** to subscription and unsubscription events.\n", "\n", @@ -430,7 +430,7 @@ "![](https://i.imgur.com/CRLzESV.png)\n", "\n", "Plus see the other\n", - "links on [RX docu](http://reactivex.io/documentation/operators/subscribeon.html)" + "links on [RX docu](http://reactivex.io/documentation/operators/subscribe_on.html)" ] }, { @@ -448,7 +448,7 @@ "\n", "========== subscribe_on ==========\n", "\n", - "module rx.linq.observable.subscribeon\n", + "module rx.linq.observable.subscribe_on\n", "@extensionmethod(ObservableBase)\n", "def subscribe_on(self, scheduler):\n", " Subscribe on the specified scheduler.\n", @@ -532,7 +532,7 @@ "collapsed": true }, "source": [ - "## ...when it notifies observers **[observe_on](http://reactivex.io/documentation/operators/subscribeon.html)**\n", + "## ...when it notifies observers **[observe_on](http://reactivex.io/documentation/operators/subscribe_on.html)**\n", "\n", "Via this you can add side effects on any notification to any subscriber.\n", "\n", diff --git a/rx/core/abc/observable.py b/rx/core/abc/observable.py index 2ca422aab..e99b96d32 100644 --- a/rx/core/abc/observable.py +++ b/rx/core/abc/observable.py @@ -1,8 +1,8 @@ from abc import ABC, abstractmethod -from typing import Callable, Generic, Optional, TypeVar +from typing import Callable, Generic, Optional, TypeVar, Union from .disposable import DisposableBase -from .observer import ObserverBase +from .observer import ObserverBase, OnNext, OnError, OnCompleted from .scheduler import SchedulerBase _T_out = TypeVar("_T_out", covariant=True) @@ -18,7 +18,9 @@ class ObservableBase(Generic[_T_out], ABC): @abstractmethod def subscribe( self, - observer: ObserverBase[_T_out], + on_next: Optional[Union[OnNext[_T_out], ObserverBase[_T_out]]] = None, + on_error: Optional[OnError] = None, + on_completed: Optional[OnCompleted] = None, *, scheduler: Optional[SchedulerBase] = None ) -> DisposableBase: diff --git a/rx/core/observable/catch.py b/rx/core/observable/catch.py index 78e1aad67..62df51140 100644 --- a/rx/core/observable/catch.py +++ b/rx/core/observable/catch.py @@ -63,8 +63,11 @@ def on_error(exn: Exception) -> None: else: d = SingleAssignmentDisposable() subscription.disposable = d - d.disposable = current.subscribe_( - observer.on_next, on_error, observer.on_completed, scheduler_ + d.disposable = current.subscribe( + observer.on_next, + on_error, + observer.on_completed, + scheduler=scheduler_, ) cancelable.disposable = _scheduler.schedule(action) diff --git a/rx/core/observable/combinelatest.py b/rx/core/observable/combinelatest.py index 2848035c5..1f6a482e5 100644 --- a/rx/core/observable/combinelatest.py +++ b/rx/core/observable/combinelatest.py @@ -62,8 +62,8 @@ def on_completed(): subscription = subscriptions[i] assert subscription - subscription.disposable = sources[i].subscribe_( - on_next, observer.on_error, on_completed, scheduler + subscription.disposable = sources[i].subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) for idx in range(n): diff --git a/rx/core/observable/concat.py b/rx/core/observable/concat.py index e07f55c05..9fffaf0a5 100644 --- a/rx/core/observable/concat.py +++ b/rx/core/observable/concat.py @@ -41,8 +41,11 @@ def on_completed(): else: d = SingleAssignmentDisposable() subscription.disposable = d - d.disposable = current.subscribe_( - observer.on_next, observer.on_error, on_completed, scheduler_ + d.disposable = current.subscribe( + observer.on_next, + observer.on_error, + on_completed, + scheduler=scheduler_, ) cancelable.disposable = _scheduler.schedule(action) diff --git a/rx/core/observable/forkjoin.py b/rx/core/observable/forkjoin.py index 1f4d46510..fad900f79 100644 --- a/rx/core/observable/forkjoin.py +++ b/rx/core/observable/forkjoin.py @@ -58,8 +58,8 @@ def on_completed() -> None: with parent.lock: done(i) - subscriptions[i].disposable = sources[i].subscribe_( - on_next, observer.on_error, on_completed, scheduler + subscriptions[i].disposable = sources[i].subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) for i in range(n): diff --git a/rx/core/observable/fromfuture.py b/rx/core/observable/fromfuture.py index b39887f69..55c0feada 100644 --- a/rx/core/observable/fromfuture.py +++ b/rx/core/observable/fromfuture.py @@ -14,7 +14,6 @@ def from_future_(future: "Future[_T]") -> Observable[_T]: Args: future -- A Python 3 compatible future. https://docs.python.org/3/library/asyncio-task.html#future - http://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.Future Returns: An Observable sequence which wraps the existing future success diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 322f3d0f8..9fdeae3d2 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -48,30 +48,11 @@ def _subscribe_core( ) -> abc.DisposableBase: return self._subscribe(observer, scheduler) if self._subscribe else Disposable() - @overload - def subscribe( - self, - observer: abc.ObserverBase[_T], - *, - scheduler: Optional[abc.SchedulerBase] = None, - ) -> abc.DisposableBase: - ... - - @overload def subscribe( self, - observer: Optional[abc.OnNext[_T]] = None, - *, - scheduler: Optional[abc.SchedulerBase] = None, - ) -> abc.DisposableBase: - ... - - def subscribe( - self, - observer: Union[abc.ObserverBase[_T], abc.OnNext[_T], None] = None, + on_next: Optional[Union[abc.ObserverBase[_T], abc.OnNext[_T], None]] = None, on_error: Optional[abc.OnError] = None, on_completed: Optional[abc.OnCompleted] = None, - on_next: Optional[abc.OnNext[_T]] = None, *, scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: @@ -107,46 +88,15 @@ def subscribe( Disposable object representing an observer's subscription to the observable sequence. """ - if observer: - if ( - isinstance(observer, abc.ObserverBase) - or hasattr(observer, "on_next") - and callable(getattr(observer, "on_next")) - ): - on_next = cast(abc.ObserverBase[_T], observer).on_next - on_error = cast(abc.ObserverBase[_T], observer).on_error - on_completed = cast(abc.ObserverBase[_T], observer).on_completed - else: - on_next = observer - return self.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) - - def subscribe_( - self, - on_next: Optional[abc.OnNext[_T]] = None, - on_error: Optional[abc.OnError] = None, - on_completed: Optional[abc.OnCompleted] = None, - scheduler: Optional[abc.SchedulerBase] = None, - ) -> abc.DisposableBase: - """Subscribe callbacks to the observable sequence. - - Examples: - >>> source.subscribe_(on_next) - >>> source.subscribe_(on_next, on_error) - >>> source.subscribe_(on_next, on_error, on_completed) - - Args: - on_next: [Optional] Action to invoke for each element in the - observable sequence. - 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. - scheduler: [Optional] The 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], on_next) + on_next = obv.on_next + on_error = obv.on_error + on_completed = obv.on_completed auto_detach_observer: AutoDetachObserver[_T] = AutoDetachObserver( on_next, on_error, on_completed diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index f9dae7909..1a8238438 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -56,8 +56,8 @@ def action(scheduler: abc.SchedulerBase, state: Optional[Exception] = None): def on_resume(state: Optional[Exception] = None): scheduler.schedule(action, state) - d.disposable = current.subscribe_( - observer.on_next, on_resume, on_resume, scheduler + d.disposable = current.subscribe( + observer.on_next, on_resume, on_resume, scheduler=scheduler ) cancelable.disposable = scheduler.schedule(action) diff --git a/rx/core/observable/withlatestfrom.py b/rx/core/observable/withlatestfrom.py index fb49f53f1..775b20f47 100644 --- a/rx/core/observable/withlatestfrom.py +++ b/rx/core/observable/withlatestfrom.py @@ -13,20 +13,20 @@ def with_latest_from_( def subscribe( observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None ) -> abc.DisposableBase: - def subscribe_all( + def subscribeall( parent: Observable[Any], *children: Observable[Any] ) -> List[SingleAssignmentDisposable]: values = [NO_VALUE for _ in children] - def subscribe_child(i: int, child: Observable[Any]): + def subscribechild(i: int, child: Observable[Any]): subscription = SingleAssignmentDisposable() def on_next(value: Any) -> None: with parent.lock: values[i] = value - subscription.disposable = child.subscribe_( + subscription.disposable = child.subscribe( on_next, observer.on_error, scheduler=scheduler ) return subscription @@ -39,18 +39,18 @@ def on_next(value: Any) -> None: result = (value,) + tuple(values) observer.on_next(result) - disp = parent.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + disp = parent.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) parent_subscription.disposable = disp children_subscription = [ - subscribe_child(i, child) for i, child in enumerate(children) + subscribechild(i, child) for i, child in enumerate(children) ] return [parent_subscription] + children_subscription - return CompositeDisposable(subscribe_all(parent, *sources)) + return CompositeDisposable(subscribeall(parent, *sources)) return Observable(subscribe) diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index b550941bf..5f1e2a9ea 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -76,8 +76,8 @@ def on_next(x: Any): queues[i].append(x) next(i) - sad.disposable = source.subscribe_( - on_next, observer.on_error, lambda: completed(i), scheduler + sad.disposable = source.subscribe( + on_next, observer.on_error, lambda: completed(i), scheduler=scheduler ) subscriptions[i] = sad diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index a7376db5e..3cc7ddc9d 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -56,8 +56,8 @@ def on_completed_left() -> None: if choice[0] == left_choice: observer.on_completed() - left_d = left_source.subscribe_( - on_next_left, on_error_left, on_completed_left, scheduler + left_d = left_source.subscribe( + on_next_left, on_error_left, on_completed_left, scheduler=scheduler ) left_subscription.disposable = left_d @@ -79,8 +79,8 @@ def on_completed_right() -> None: if choice[0] == right_choice: observer.on_completed() - right_d = obs.subscribe_( - send_right, on_error_right, on_completed_right, scheduler + right_d = obs.subscribe( + send_right, on_error_right, on_completed_right, scheduler=scheduler ) right_subscription.disposable = right_d return CompositeDisposable(left_subscription, right_subscription) diff --git a/rx/core/operators/catch.py b/rx/core/operators/catch.py index f1f48c46a..d453876d6 100644 --- a/rx/core/operators/catch.py +++ b/rx/core/operators/catch.py @@ -1,34 +1,39 @@ -from typing import Callable, Union +from asyncio import Future +from typing import Callable, Optional, TypeVar, Union import rx -from rx.core import Observable, abc, typing +from rx.core import Observable, abc from rx.disposable import SerialDisposable, SingleAssignmentDisposable -from rx.internal.utils import is_future + +_T = TypeVar("_T") def catch_handler( - source: Observable, handler: Callable[[Exception, Observable], Observable] -) -> Observable: - def subscribe(observer, scheduler=None): + source: Observable[_T], + handler: Callable[[Exception, Observable[_T]], Union[Observable[_T], "Future[_T]"]], +) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: d1 = SingleAssignmentDisposable() subscription = SerialDisposable() subscription.disposable = d1 - def on_error(exception): + def on_error(exception: Exception) -> None: try: result = handler(exception, source) except Exception as ex: # By design. pylint: disable=W0703 observer.on_error(ex) return - result = rx.from_future(result) if is_future(result) else result + result = rx.from_future(result) if isinstance(result, Future) else result d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = result.subscribe(observer, scheduler=scheduler) - d1.disposable = source.subscribe_( - observer.on_next, on_error, observer.on_completed, scheduler + d1.disposable = source.subscribe( + observer.on_next, on_error, observer.on_completed, scheduler=scheduler ) return subscription @@ -36,9 +41,11 @@ def on_error(exception): def _catch( - handler: Union[Observable, Callable[[Exception, Observable], Observable]] -) -> Callable[[Observable], Observable]: - def catch(source: Observable) -> Observable: + handler: Union[ + Observable[_T], Callable[[Exception, Observable[_T]], Observable[_T]] + ] +) -> Callable[[Observable[_T]], Observable[_T]]: + def catch(source: Observable[_T]) -> Observable[_T]: """Continues an observable sequence that is terminated by an exception with the next observable sequence. @@ -64,7 +71,11 @@ def catch(source: Observable) -> Observable: return rx.catch(source, handler) else: raise TypeError( - "catch operator takes whether an Observable or a callable handler as argument." + "catch operator takes whether an Observable, " + "or a callable handler as argument." ) return catch + + +__all__ = ["_catch"] diff --git a/rx/core/operators/debounce.py b/rx/core/operators/debounce.py index 2f5eaf02e..68da5c50f 100644 --- a/rx/core/operators/debounce.py +++ b/rx/core/operators/debounce.py @@ -69,7 +69,7 @@ def on_completed() -> None: has_value[0] = False _id[0] += 1 - subscription = source.subscribe_( + subscription = source.subscribe( on_next, on_error, on_completed, scheduler=scheduler_ ) return CompositeDisposable(subscription, cancelable) @@ -140,7 +140,7 @@ def on_completed() -> None: has_value = False d.dispose() - d.disposable = throttle.subscribe_( + d.disposable = throttle.subscribe( on_next, observer.on_error, on_completed, scheduler=scheduler ) @@ -161,7 +161,7 @@ def on_completed() -> None: has_value = False _id[0] += 1 - subscription = source.subscribe_( + subscription = source.subscribe( on_next, on_error, on_completed, scheduler=scheduler ) return CompositeDisposable(subscription, cancelable) diff --git a/rx/core/operators/defaultifempty.py b/rx/core/operators/defaultifempty.py index 1c5e56463..3af0682ec 100644 --- a/rx/core/operators/defaultifempty.py +++ b/rx/core/operators/defaultifempty.py @@ -40,8 +40,8 @@ def on_completed(): observer.on_next(default_value) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 4b22cd12a..f6967ddbc 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -108,7 +108,7 @@ def action(scheduler, state): mad.disposable = _scheduler.schedule_relative(duetime, action) - subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe_( + subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe( on_next, scheduler=_scheduler ) diff --git a/rx/core/operators/delaywithmapper.py b/rx/core/operators/delaywithmapper.py index 50a06a3a1..b6930f268 100644 --- a/rx/core/operators/delaywithmapper.py +++ b/rx/core/operators/delaywithmapper.py @@ -79,8 +79,8 @@ def on_completed() -> None: delays.remove(d) done() - d.disposable = delay.subscribe_( - on_next, observer.on_error, on_completed, scheduler + d.disposable = delay.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) def on_completed() -> None: @@ -88,14 +88,14 @@ def on_completed() -> None: subscription.dispose() done() - subscription.disposable = source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + subscription.disposable = source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) if not sub_delay: start() else: - subscription.disposable = sub_delay.subscribe_( + subscription.disposable = sub_delay.subscribe( lambda _: start(), observer.on_error, start ) diff --git a/rx/core/operators/dematerialize.py b/rx/core/operators/dematerialize.py index 4e9bef990..e6041b2f5 100644 --- a/rx/core/operators/dematerialize.py +++ b/rx/core/operators/dematerialize.py @@ -24,8 +24,8 @@ def subscribe( def on_next(value: Notification[_T]) -> None: return value.accept(observer) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index d08d0329d..60484514e 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -71,8 +71,8 @@ def on_next(x: _T) -> None: hashset.push(key) and observer.on_next(x) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/distinctuntilchanged.py b/rx/core/operators/distinctuntilchanged.py index 6b266c0d0..69e5b7362 100644 --- a/rx/core/operators/distinctuntilchanged.py +++ b/rx/core/operators/distinctuntilchanged.py @@ -68,7 +68,7 @@ def on_next(value: _T) -> None: current_key = key observer.on_next(value) - return source.subscribe_( + return source.subscribe( on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) diff --git a/rx/core/operators/do.py b/rx/core/operators/do.py index f9e7a5d20..c7f94bc35 100644 --- a/rx/core/operators/do.py +++ b/rx/core/operators/do.py @@ -74,7 +74,9 @@ def _on_completed() -> None: observer.on_completed() - return source.subscribe_(_on_next, _on_error, _on_completed, scheduler) + return source.subscribe( + _on_next, _on_error, _on_completed, scheduler=scheduler + ) return Observable(subscribe) @@ -117,7 +119,7 @@ def on_next(value): except Exception as e: # pylint: disable=broad-except observer.on_error(e) - return source.subscribe_(on_next, observer.on_error, observer.on_completed) + return source.subscribe(on_next, observer.on_error, observer.on_completed) return Observable(subscribe) @@ -134,8 +136,11 @@ def do_on_subscribe(source: Observable, on_subscribe): def subscribe(observer, scheduler=None): on_subscribe() - return source.subscribe_( - observer.on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + observer.on_next, + observer.on_error, + observer.on_completed, + scheduler=scheduler, ) return Observable(subscribe) @@ -158,8 +163,11 @@ def dispose(self) -> None: def subscribe(observer, scheduler=None): composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose()) - subscription = source.subscribe_( - observer.on_next, observer.on_error, observer.on_completed, scheduler + subscription = source.subscribe( + observer.on_next, + observer.on_error, + observer.on_completed, + scheduler=scheduler, ) composite_disposable.add(subscription) return composite_disposable @@ -193,7 +201,9 @@ def on_error(exception): else: observer.on_error(exception) - return source.subscribe_(observer.on_next, on_error, on_completed, scheduler) + return source.subscribe( + observer.on_next, on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) @@ -222,7 +232,9 @@ def on_error(exception): except Exception as err: # pylint: disable=broad-except observer.on_error(err) - return source.subscribe(observer.on_next, on_error, on_completed, scheduler) + return source.subscribe( + observer.on_next, on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) @@ -276,8 +288,8 @@ def on_error(exception): composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose(was_invoked)) - subscription = source.subscribe_( - observer.on_next, on_error, on_completed, scheduler + subscription = source.subscribe( + observer.on_next, on_error, on_completed, scheduler=scheduler ) composite_disposable.add(subscription) diff --git a/rx/core/operators/elementatordefault.py b/rx/core/operators/elementatordefault.py index ede12fde7..85a33cd36 100644 --- a/rx/core/operators/elementatordefault.py +++ b/rx/core/operators/elementatordefault.py @@ -38,8 +38,8 @@ def on_completed(): observer.on_next(cast(_T, default_value)) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/exclusive.py b/rx/core/operators/exclusive.py index 4f4f433fc..3413a70e2 100644 --- a/rx/core/operators/exclusive.py +++ b/rx/core/operators/exclusive.py @@ -49,7 +49,7 @@ def on_completed_inner(): if is_stopped[0] and len(g) == 1: observer.on_completed() - inner_subscription.disposable = inner_source.subscribe_( + inner_subscription.disposable = inner_source.subscribe( observer.on_next, observer.on_error, on_completed_inner, @@ -61,8 +61,8 @@ def on_completed() -> None: if not has_current[0] and len(g) == 1: observer.on_completed() - m.disposable = source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + m.disposable = source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return g diff --git a/rx/core/operators/expand.py b/rx/core/operators/expand.py index f3493f88a..312e613a1 100644 --- a/rx/core/operators/expand.py +++ b/rx/core/operators/expand.py @@ -81,8 +81,8 @@ def on_complete() -> None: if active_count == 0: observer.on_completed() - sad.disposable = work.subscribe_( - on_next, observer.on_error, on_complete, scheduler + sad.disposable = work.subscribe( + on_next, observer.on_error, on_complete, scheduler=scheduler ) m.disposable = scheduler.schedule(action) diff --git a/rx/core/operators/filter.py b/rx/core/operators/filter.py index 3bc0d9e2f..57f39878d 100644 --- a/rx/core/operators/filter.py +++ b/rx/core/operators/filter.py @@ -37,8 +37,8 @@ def on_next(value: _T): if should_run: observer.on_next(value) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) @@ -83,8 +83,8 @@ def on_next(value: _T): if should_run: observer.on_next(value) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/find.py b/rx/core/operators/find.py index bb86ed928..0db18551a 100644 --- a/rx/core/operators/find.py +++ b/rx/core/operators/find.py @@ -34,8 +34,8 @@ def on_completed(): observer.on_next(-1 if yield_index else None) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/firstordefault.py b/rx/core/operators/firstordefault.py index 714f1381c..ab830128a 100644 --- a/rx/core/operators/firstordefault.py +++ b/rx/core/operators/firstordefault.py @@ -27,8 +27,8 @@ def on_completed(): observer.on_next(cast(_T, default_value)) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index f30d928b6..88075bbfa 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -130,8 +130,8 @@ def on_error(exn: Exception) -> None: def on_completed(): expire() - sad.disposable = duration.pipe(ops.take(1)).subscribe_( - on_next, on_error, on_completed, scheduler + sad.disposable = duration.pipe(ops.take(1)).subscribe( + on_next, on_error, on_completed, scheduler=scheduler ) try: @@ -158,7 +158,7 @@ def on_completed() -> None: observer.on_completed() group_disposable.add( - source.subscribe_(on_next, on_error, on_completed, scheduler) + source.subscribe(on_next, on_error, on_completed, scheduler=scheduler) ) return ref_count_disposable diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index 3114fe74c..349eeaf24 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -97,8 +97,8 @@ def on_error(error): observer.on_error(error) - md.disposable = duration.pipe(ops.take(1)).subscribe_( - nothing, on_error, expire, scheduler + md.disposable = duration.pipe(ops.take(1)).subscribe( + nothing, on_error, expire, scheduler=scheduler ) def on_error_left(error): @@ -108,8 +108,11 @@ def on_error_left(error): observer.on_error(error) group.add( - left.subscribe_( - on_next_left, on_error_left, observer.on_completed, scheduler + left.subscribe( + on_next_left, + on_error_left, + observer.on_completed, + scheduler=scheduler, ) ) @@ -142,8 +145,8 @@ def on_error(error): observer.on_error(error) - md.disposable = duration.pipe(ops.take(1)).subscribe_( - nothing, on_error, expire, scheduler + md.disposable = duration.pipe(ops.take(1)).subscribe( + nothing, on_error, expire, scheduler=scheduler ) with left.lock: @@ -156,7 +159,7 @@ def on_error_right(error): observer.on_error(error) - group.add(right.subscribe_(send_right, on_error_right, scheduler=scheduler)) + group.add(right.subscribe(send_right, on_error_right, scheduler=scheduler)) return rcd return Observable(subscribe) diff --git a/rx/core/operators/ignoreelements.py b/rx/core/operators/ignoreelements.py index 5b10c8667..ba8d0dee6 100644 --- a/rx/core/operators/ignoreelements.py +++ b/rx/core/operators/ignoreelements.py @@ -20,8 +20,8 @@ def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - return source.subscribe_( - noop, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + noop, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/join.py b/rx/core/operators/join.py index e9f71c896..31f57c947 100644 --- a/rx/core/operators/join.py +++ b/rx/core/operators/join.py @@ -60,8 +60,8 @@ def expire(): observer.on_error(exception) return - md.disposable = duration.pipe(take(1)).subscribe_( - noop, observer.on_error, lambda: expire(), scheduler + md.disposable = duration.pipe(take(1)).subscribe( + noop, observer.on_error, lambda: expire(), scheduler=scheduler ) for val in right_map.values(): @@ -75,8 +75,11 @@ def on_completed_left(): observer.on_completed() group.add( - left.subscribe_( - on_next_left, observer.on_error, on_completed_left, scheduler + left.subscribe( + on_next_left, + observer.on_error, + on_completed_left, + scheduler=scheduler, ) ) @@ -103,8 +106,8 @@ def expire(): observer.on_error(exception) return - md.disposable = duration.pipe(take(1)).subscribe_( - noop, observer.on_error, lambda: expire(), scheduler + md.disposable = duration.pipe(take(1)).subscribe( + noop, observer.on_error, lambda: expire(), scheduler=scheduler ) for val in left_map.values(): @@ -118,8 +121,11 @@ def on_completed_right(): observer.on_completed() group.add( - right.subscribe_( - on_next_right, observer.on_error, on_completed_right, scheduler + right.subscribe( + on_next_right, + observer.on_error, + on_completed_right, + scheduler=scheduler, ) ) return group diff --git a/rx/core/operators/lastordefault.py b/rx/core/operators/lastordefault.py index 988765c76..750c79e01 100644 --- a/rx/core/operators/lastordefault.py +++ b/rx/core/operators/lastordefault.py @@ -27,7 +27,9 @@ def on_completed(): observer.on_next(value[0]) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) diff --git a/rx/core/operators/map.py b/rx/core/operators/map.py index 3a205c803..3f0b69c61 100644 --- a/rx/core/operators/map.py +++ b/rx/core/operators/map.py @@ -46,7 +46,9 @@ def on_next(value: _T1) -> None: else: obv.on_next(result) - return source.subscribe_(on_next, obv.on_error, obv.on_completed, scheduler) + return source.subscribe( + on_next, obv.on_error, obv.on_completed, scheduler=scheduler + ) return Observable(subscribe) @@ -56,8 +58,8 @@ def on_next(value: _T1) -> None: def map_indexed( mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: - def _identity(value: Any, _: int) -> Any: - return value + def _identity(value: _T1, _: int) -> _T2: + return cast(_T2, value) _mapper_indexed = mapper_indexed or _identity diff --git a/rx/core/operators/materialize.py b/rx/core/operators/materialize.py index 1351cbeef..a6c5e1c5e 100644 --- a/rx/core/operators/materialize.py +++ b/rx/core/operators/materialize.py @@ -36,7 +36,9 @@ def on_completed() -> None: observer.on_next(OnCompleted()) observer.on_completed() - return source.subscribe_(on_next, on_error, on_completed, scheduler) + return source.subscribe( + on_next, on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) diff --git a/rx/core/operators/merge.py b/rx/core/operators/merge.py index efe527c39..4bbe4a5b1 100644 --- a/rx/core/operators/merge.py +++ b/rx/core/operators/merge.py @@ -60,8 +60,8 @@ def on_completed(): on_next = synchronized(source.lock)(observer.on_next) on_error = synchronized(source.lock)(observer.on_error) - subscription.disposable = xs.subscribe_( - on_next, on_error, on_completed, scheduler + subscription.disposable = xs.subscribe( + on_next, on_error, on_completed, scheduler=scheduler ) def on_next(inner_source: Observable[_T]) -> None: @@ -78,7 +78,9 @@ def on_completed(): observer.on_completed() group.add( - source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler + ) ) return group @@ -129,8 +131,8 @@ def on_completed(): on_next: typing.OnNext[_T] = synchronized(source.lock)(observer.on_next) on_error = synchronized(source.lock)(observer.on_error) - subscription = inner_source.subscribe_( - on_next, on_error, on_completed, scheduler + subscription = inner_source.subscribe( + on_next, on_error, on_completed, scheduler=scheduler ) inner_subscription.disposable = subscription @@ -139,8 +141,8 @@ def on_completed(): if len(group) == 1: observer.on_completed() - m.disposable = source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + m.disposable = source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return group diff --git a/rx/core/operators/minby.py b/rx/core/operators/minby.py index 2978ab86f..355bd3aa8 100644 --- a/rx/core/operators/minby.py +++ b/rx/core/operators/minby.py @@ -51,7 +51,9 @@ def on_completed(): observer.on_next(items) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) diff --git a/rx/core/operators/pairwise.py b/rx/core/operators/pairwise.py index 3a2be9365..5a8f13859 100644 --- a/rx/core/operators/pairwise.py +++ b/rx/core/operators/pairwise.py @@ -43,7 +43,7 @@ def on_next(x: _T) -> None: if pair: observer.on_next(pair) - return source.subscribe_(on_next, observer.on_error, observer.on_completed) + return source.subscribe(on_next, observer.on_error, observer.on_completed) return Observable(subscribe) diff --git a/rx/core/operators/sample.py b/rx/core/operators/sample.py index f7a5e70f6..17aaa8978 100644 --- a/rx/core/operators/sample.py +++ b/rx/core/operators/sample.py @@ -36,9 +36,14 @@ def on_completed(): at_end = True return CompositeDisposable( - source.subscribe_(on_next, observer.on_error, on_completed, scheduler), - sampler.subscribe_( - sample_subscribe, observer.on_error, sample_subscribe, scheduler + source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler + ), + sampler.subscribe( + sample_subscribe, + observer.on_error, + sample_subscribe, + scheduler=scheduler, ), ) diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index 60de61328..12f6c885f 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -104,11 +104,11 @@ def on_completed2(): observer.on_next(True) observer.on_completed() - subscription1 = first.subscribe_( - on_next1, observer.on_error, on_completed1, scheduler + subscription1 = first.subscribe( + on_next1, observer.on_error, on_completed1, scheduler=scheduler ) - subscription2 = second.subscribe_( - on_next2, observer.on_error, on_completed2, scheduler + subscription2 = second.subscribe( + on_next2, observer.on_error, on_completed2, scheduler=scheduler ) return CompositeDisposable(subscription1, subscription2) diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index 909aa5da3..d3969308c 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -35,8 +35,8 @@ def on_completed(): observer.on_next(value[0]) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/skip.py b/rx/core/operators/skip.py index fe9532744..c7ddb2a20 100644 --- a/rx/core/operators/skip.py +++ b/rx/core/operators/skip.py @@ -38,8 +38,8 @@ def on_next(value: _T) -> None: else: remaining -= 1 - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/skiplast.py b/rx/core/operators/skiplast.py index 4bf1ff3bc..b6a4a9ea4 100644 --- a/rx/core/operators/skiplast.py +++ b/rx/core/operators/skiplast.py @@ -40,8 +40,8 @@ def on_next(value: _T) -> None: if front is not None: observer.on_next(front) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/skiplastwithtime.py b/rx/core/operators/skiplastwithtime.py index 586b21e44..63b1a59af 100644 --- a/rx/core/operators/skiplastwithtime.py +++ b/rx/core/operators/skiplastwithtime.py @@ -57,8 +57,8 @@ def on_completed() -> None: observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, _scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=_scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index 0d3a6a547..eaf6c9c89 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -45,8 +45,8 @@ def on_completed() -> None: if is_open[0]: observer.on_completed() - subs = source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + subs = source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) subscriptions = CompositeDisposable(subs) @@ -60,8 +60,8 @@ def on_next2(x: Any) -> None: def on_completed2(): right_subscription.dispose() - right_subscription.disposable = obs.subscribe_( - on_next2, observer.on_error, on_completed2, scheduler + right_subscription.disposable = obs.subscribe( + on_next2, observer.on_error, on_completed2, scheduler=scheduler ) return subscriptions diff --git a/rx/core/operators/skipuntilwithtime.py b/rx/core/operators/skipuntilwithtime.py index cb987fb52..878d6192e 100644 --- a/rx/core/operators/skipuntilwithtime.py +++ b/rx/core/operators/skipuntilwithtime.py @@ -51,8 +51,8 @@ def on_next(x: _T) -> None: if open[0]: observer.on_next(x) - subscription = source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler_ + subscription = source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler_ ) def action(scheduler: abc.SchedulerBase, state: Any): diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index 0c2a064df..baa5661c9 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -46,8 +46,8 @@ def on_next(value: _T): if running: observer.on_next(value) - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/skipwithtime.py b/rx/core/operators/skipwithtime.py index 3643ac3bf..e5443c504 100644 --- a/rx/core/operators/skipwithtime.py +++ b/rx/core/operators/skipwithtime.py @@ -53,8 +53,8 @@ def on_next(x: _T): if open[0]: observer.on_next(x) - d = source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler_ + d = source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler_ ) return CompositeDisposable(t, d) diff --git a/rx/core/operators/some.py b/rx/core/operators/some.py index bf459995d..5d1eeaee4 100644 --- a/rx/core/operators/some.py +++ b/rx/core/operators/some.py @@ -41,7 +41,9 @@ def on_error(): observer.on_next(False) observer.on_completed() - return source.subscribe_(on_next, observer.on_error, on_error, scheduler) + return source.subscribe( + on_next, observer.on_error, on_error, scheduler=scheduler + ) if predicate: return source.pipe( diff --git a/rx/core/operators/switchlatest.py b/rx/core/operators/switchlatest.py index e88b0844f..307aace34 100644 --- a/rx/core/operators/switchlatest.py +++ b/rx/core/operators/switchlatest.py @@ -69,7 +69,7 @@ def on_completed() -> None: if is_stopped[0]: observer.on_completed() - d.disposable = obs.subscribe_( + d.disposable = obs.subscribe( on_next, on_error, on_completed, scheduler=scheduler ) @@ -78,7 +78,7 @@ def on_completed() -> None: if not has_latest[0]: observer.on_completed() - subscription = source.subscribe_( + subscription = source.subscribe( on_next, observer.on_error, on_completed, scheduler=scheduler ) return CompositeDisposable(subscription, inner_subscription) diff --git a/rx/core/operators/take.py b/rx/core/operators/take.py index b98851d24..0a0c58bc8 100644 --- a/rx/core/operators/take.py +++ b/rx/core/operators/take.py @@ -42,8 +42,8 @@ def on_next(value: _T) -> None: if not remaining: observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/takelast.py b/rx/core/operators/takelast.py index d154adeb0..f0b337e52 100644 --- a/rx/core/operators/takelast.py +++ b/rx/core/operators/takelast.py @@ -43,8 +43,8 @@ def on_completed(): observer.on_next(q.pop(0)) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/takelastbuffer.py b/rx/core/operators/takelastbuffer.py index c29c0762f..9455ee753 100644 --- a/rx/core/operators/takelastbuffer.py +++ b/rx/core/operators/takelastbuffer.py @@ -43,8 +43,8 @@ def on_completed() -> None: observer.on_next(q) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/takelastwithtime.py b/rx/core/operators/takelastwithtime.py index 2f3acc6ae..7ef8167ab 100644 --- a/rx/core/operators/takelastwithtime.py +++ b/rx/core/operators/takelastwithtime.py @@ -56,8 +56,8 @@ def on_completed(): observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler_ + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler_ ) return Observable(subscribe) diff --git a/rx/core/operators/takeuntil.py b/rx/core/operators/takeuntil.py index 93f675138..1717a1de7 100644 --- a/rx/core/operators/takeuntil.py +++ b/rx/core/operators/takeuntil.py @@ -39,7 +39,9 @@ def on_completed(_: _T) -> None: return CompositeDisposable( source.subscribe(observer), - obs.subscribe_(on_completed, observer.on_error, noop, scheduler), + obs.subscribe( + on_completed, observer.on_error, noop, scheduler=scheduler + ), ) return Observable(subscribe) diff --git a/rx/core/operators/takewhile.py b/rx/core/operators/takewhile.py index b56b0f9cd..e4ed4e4ca 100644 --- a/rx/core/operators/takewhile.py +++ b/rx/core/operators/takewhile.py @@ -51,8 +51,8 @@ def on_next(value: _T): observer.on_next(value) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) @@ -109,8 +109,8 @@ def on_next(value: Any) -> None: observer.on_next(value) observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/throttlefirst.py b/rx/core/operators/throttlefirst.py index 6d7c99250..cdbd921ef 100644 --- a/rx/core/operators/throttlefirst.py +++ b/rx/core/operators/throttlefirst.py @@ -45,7 +45,7 @@ def on_next(x: _T) -> None: if emit: observer.on_next(x) - return source.subscribe_( + return source.subscribe( on_next, observer.on_error, observer.on_completed, scheduler=_scheduler ) diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index 998b6821d..a4a3dc327 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -94,8 +94,8 @@ def on_completed(): _id[0] += 1 observer.on_completed() - original.disposable = source.subscribe_( - on_next, on_error, on_completed, scheduler_ + original.disposable = source.subscribe( + on_next, on_error, on_completed, scheduler=scheduler_ ) return CompositeDisposable(subscription, timer) diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index f2f27ed20..7d3139ba7 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -80,8 +80,8 @@ def on_completed() -> None: if timer_wins(): subscription.disposable = other.subscribe(observer) - d.disposable = timeout.subscribe_( - on_next, on_error, on_completed, scheduler + d.disposable = timeout.subscribe( + on_next, on_error, on_completed, scheduler=scheduler ) set_timer(first_timeout) @@ -113,8 +113,8 @@ def on_completed() -> None: if observer_wins(): observer.on_completed() - original.disposable = source.subscribe_( - on_next, on_error, on_completed, scheduler + original.disposable = source.subscribe( + on_next, on_error, on_completed, scheduler=scheduler ) return CompositeDisposable(subscription, timer) diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index 2ea8ba3e0..d90976ece 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -51,8 +51,8 @@ def on_completed() -> None: m = dict() observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index 114d93e45..b39a87e91 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -53,7 +53,7 @@ def on_completed(): future.set_exception(SequenceContainsNoElementsError()) last_value = None - dis = source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) + dis = source.subscribe(on_next, on_error, on_completed, scheduler=scheduler) future.add_done_callback(lambda _: dis.dispose()) return future diff --git a/rx/core/operators/toiterable.py b/rx/core/operators/toiterable.py index 2c29172a2..f28b14240 100644 --- a/rx/core/operators/toiterable.py +++ b/rx/core/operators/toiterable.py @@ -32,8 +32,8 @@ def on_completed(): queue = [] observer.on_completed() - return source.subscribe_( - on_next, observer.on_error, on_completed, scheduler + return source.subscribe( + on_next, observer.on_error, on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index 9149fdf7c..d02aa1825 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -58,7 +58,7 @@ def on_completed(): observer.on_next("".join(n for n in result)) observer.on_completed() - return source.subscribe_(on_next, on_error, on_completed) + return source.subscribe(on_next, on_error, on_completed) return Observable(subscribe) diff --git a/rx/core/operators/toset.py b/rx/core/operators/toset.py index a055d2c36..e5f395533 100644 --- a/rx/core/operators/toset.py +++ b/rx/core/operators/toset.py @@ -25,7 +25,9 @@ def on_completed() -> None: s = set() observer.on_completed() - return source.subscribe_(s.add, observer.on_error, on_completed, scheduler) + return source.subscribe( + s.add, observer.on_error, on_completed, scheduler=scheduler + ) return Observable(subscribe) diff --git a/rx/core/operators/window.py b/rx/core/operators/window.py index 901b5c49c..46915d99f 100644 --- a/rx/core/operators/window.py +++ b/rx/core/operators/window.py @@ -19,9 +19,9 @@ _T = TypeVar("_T") -def _window_toggle( - openings: Observable, closing_mapper: Callable[[Any], Observable] -) -> Callable[[Observable], Observable]: +def window_toggle_( + openings: Observable[Any], closing_mapper: Callable[[Any], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -32,7 +32,7 @@ def _window_toggle( An observable sequence of windows. """ - def window_toggle(source: Observable) -> Observable: + def window_toggle(source: Observable[_T]) -> Observable[Observable[_T]]: def mapper(args): _, window = args return window @@ -49,7 +49,7 @@ def mapper(args): return window_toggle -def _window( +def window_( boundaries: Observable[Any], ) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or @@ -84,7 +84,11 @@ def on_completed() -> None: window_subject.on_completed() observer.on_completed() - d.add(source.subscribe_(on_next_window, on_error, on_completed, scheduler)) + d.add( + source.subscribe( + on_next_window, on_error, on_completed, scheduler=scheduler + ) + ) def on_next_observer(w: Observable[_T]): nonlocal window_subject @@ -93,8 +97,8 @@ def on_next_observer(w: Observable[_T]): observer.on_next(add_ref(window_subject, r)) d.add( - boundaries.subscribe_( - on_next_observer, on_error, on_completed, scheduler + boundaries.subscribe( + on_next_observer, on_error, on_completed, scheduler=scheduler ) ) return r @@ -104,8 +108,8 @@ def on_next_observer(w: Observable[_T]): return window -def _window_when( - closing_mapper: Callable[[], Observable[_T]] +def window_when_( + closing_mapper: Callable[[], Observable[Any]] ) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -140,7 +144,9 @@ def on_completed() -> None: window.on_completed() observer.on_completed() - d.add(source.subscribe_(on_next, on_error, on_completed, scheduler)) + d.add( + source.subscribe(on_next, on_error, on_completed, scheduler=scheduler) + ) def create_window_on_completed(): try: @@ -158,8 +164,8 @@ def on_completed(): m1 = SingleAssignmentDisposable() m.disposable = m1 - m1.disposable = window_close.pipe(ops.take(1)).subscribe_( - noop, on_error, on_completed, scheduler + m1.disposable = window_close.pipe(ops.take(1)).subscribe( + noop, on_error, on_completed, scheduler=scheduler ) create_window_on_completed() @@ -168,3 +174,6 @@ def on_completed(): return Observable(subscribe) return window_when + + +__all__ = ["window_", "window_when_", "window_toggle_"] diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index 8b93d8475..b7fead1f8 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -81,7 +81,9 @@ def on_completed() -> None: q.pop(0).on_completed() observer.on_completed() - m.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler) + m.disposable = source.subscribe( + on_next, on_error, on_completed, scheduler=scheduler + ) return refCountDisposable return Observable(subscribe) diff --git a/rx/core/operators/windowwithtime.py b/rx/core/operators/windowwithtime.py index a94395980..19b6a220d 100644 --- a/rx/core/operators/windowwithtime.py +++ b/rx/core/operators/windowwithtime.py @@ -106,7 +106,7 @@ def on_completed() -> None: observer.on_completed() group_disposable.add( - source.subscribe_(on_next, on_error, on_completed, scheduler_) + source.subscribe(on_next, on_error, on_completed, scheduler=scheduler_) ) return ref_count_disposable diff --git a/rx/core/operators/windowwithtimeorcount.py b/rx/core/operators/windowwithtimeorcount.py index 62f14e9b9..831ae3564 100644 --- a/rx/core/operators/windowwithtimeorcount.py +++ b/rx/core/operators/windowwithtimeorcount.py @@ -82,7 +82,7 @@ def on_completed() -> None: observer.on_completed() group_disposable.add( - source.subscribe_(on_next, on_error, on_completed, scheduler_) + source.subscribe(on_next, on_error, on_completed, scheduler=scheduler_) ) return ref_count_disposable diff --git a/rx/core/operators/zip.py b/rx/core/operators/zip.py index e407b9ddc..f5cbdb458 100644 --- a/rx/core/operators/zip.py +++ b/rx/core/operators/zip.py @@ -72,8 +72,8 @@ def on_next(left: _T) -> None: result = (left, right) observer.on_next(result) - return first.subscribe_( - on_next, observer.on_error, observer.on_completed, scheduler + return first.subscribe( + on_next, observer.on_error, observer.on_completed, scheduler=scheduler ) return Observable(subscribe) diff --git a/rx/core/run.py b/rx/core/run.py index 6f33ae18b..5bd3837b8 100644 --- a/rx/core/run.py +++ b/rx/core/run.py @@ -1,5 +1,5 @@ import threading -from typing import Any, Optional, TypeVar +from typing import Any, Optional, TypeVar, cast from rx.internal.exceptions import SequenceContainsNoElementsError from rx.scheduler import NewThreadScheduler @@ -35,10 +35,10 @@ def run(source: Observable[_T]) -> _T: exception: Optional[Exception] = None latch = threading.Event() has_result = False - result: Any = None + result: _T = cast(_T, None) done = False - def on_next(value: Any) -> None: + def on_next(value: _T) -> None: nonlocal result, has_result result = value has_result = True @@ -55,13 +55,13 @@ def on_completed() -> None: done = True latch.set() - source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler) + source.subscribe(on_next, on_error, on_completed, scheduler=scheduler) while not done: latch.wait() - if isinstance(exception, Exception): - raise exception # pylint: disable=raising-bad-type + if exception: + raise exception # pylint: disable=raidfsing-bad-type if not has_result: raise SequenceContainsNoElementsError diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 7604100e9..39b30115b 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -362,8 +362,10 @@ def buffer_with_time_or_count( def catch( - handler: Union[Observable, Callable[[Exception, Observable], Observable]] -) -> Callable[[Observable], Observable]: + handler: Union[ + Observable[_T], Callable[[Exception, Observable[_T]], Observable[_T]] + ] +) -> Callable[[Observable[_T]], Observable[_T]]: """Continues an observable sequence that is terminated by an exception with the next observable sequence. @@ -2496,7 +2498,8 @@ def sequence_equal( >>> res = sequence_equal([1,2,3]) >>> res = sequence_equal([{ "value": 42 }], lambda x, y: x.value == y.value) >>> res = sequence_equal(rx.return_value(42)) - >>> res = sequence_equal(rx.return_value({ "value": 42 }), lambda x, y: x.value == y.value) + >>> res = sequence_equal( + rx.return_value({ "value": 42 }), lambda x, y: x.value == y.value) Args: second: Second observable sequence or iterable to compare. @@ -3595,7 +3598,9 @@ def timeout_with_mapper( Examples: >>> res = timeout_with_mapper(rx.timer(0.5)) >>> res = timeout_with_mapper(rx.timer(0.5), lambda x: rx.timer(0.2)) - >>> res = timeout_with_mapper(rx.timer(0.5), lambda x: rx.timer(0.2)), rx.return_value(42)) + >>> res = timeout_with_mapper( + rx.timer(0.5), lambda x: rx.timer(0.2)), rx.return_value(42) + ) Args: first_timeout: [Optional] Observable sequence that represents @@ -3783,14 +3788,14 @@ def window( returns an observable sequence of windows. """ - from rx.core.operators.window import _window + from rx.core.operators.window import window_ - return _window(boundaries) + return window_(boundaries) def window_when( - closing_mapper: Callable[[], Observable] -) -> Callable[[Observable], Observable]: + closing_mapper: Callable[[], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -3821,14 +3826,14 @@ def window_when( An operator function that takes an observable source and returns an observable sequence of windows. """ - from rx.core.operators.window import _window_when + from rx.core.operators.window import window_when_ - return _window_when(closing_mapper) + return window_when_(closing_mapper) def window_toggle( - openings: Observable, closing_mapper: Callable[[Any], Observable] -) -> Callable[[Observable], Observable]: + openings: Observable[Any], closing_mapper: Callable[[Any], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[Observable[_T]]]: """Projects each element of an observable sequence into zero or more windows. @@ -3857,9 +3862,9 @@ def window_toggle( An operator function that takes an observable source and returns an observable sequence of windows. """ - from rx.core.operators.window import _window_toggle + from rx.core.operators.window import window_toggle_ - return _window_toggle(openings, closing_mapper) + return window_toggle_(openings, closing_mapper) def window_with_count( diff --git a/tests/test_observable/test_connectableobservable.py b/tests/test_observable/test_connectableobservable.py index 7fc9c4f75..ed9cb8e43 100644 --- a/tests/test_observable/test_connectableobservable.py +++ b/tests/test_observable/test_connectableobservable.py @@ -74,7 +74,11 @@ def test_connectable_observable_connected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), ) subject = MySubject() @@ -84,13 +88,23 @@ def test_connectable_observable_connected(self): res = scheduler.start(lambda: conn) - assert res.messages == [on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250)] + assert res.messages == [ + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), + ] def test_connectable_observable_not_connected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), ) subject = MySubject() @@ -105,7 +119,11 @@ def test_connectable_observable_disconnected(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), ) subject = MySubject() @@ -122,7 +140,11 @@ def test_connectable_observable_disconnect_future(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), ) subject = MySubject() @@ -217,9 +239,19 @@ def action31(scheduler, state): res = scheduler.start(lambda: conn) - assert res.messages == [on_next(230, 3), on_next(240, 4), on_next(250, 5), on_next(280, 8), on_next(290, 9)] + assert res.messages == [ + on_next(230, 3), + on_next(240, 4), + on_next(250, 5), + on_next(280, 8), + on_next(290, 9), + ] - assert xs.subscriptions == [subscribe(225, 241), subscribe(249, 255), subscribe(275, 295)] + assert xs.subscriptions == [ + subscribe(225, 241), + subscribe(249, 255), + subscribe(275, 295), + ] def test_connectable_observable_forward_scheduler(self): scheduler = TestScheduler() diff --git a/tests/test_observable/test_fromiterable.py b/tests/test_observable/test_fromiterable.py index 0f08ce2e0..7061f5812 100644 --- a/tests/test_observable/test_fromiterable.py +++ b/tests/test_observable/test_fromiterable.py @@ -37,7 +37,8 @@ def create(): on_next(200, 3), on_next(200, 4), on_next(200, 5), - on_completed(200)] + on_completed(200), + ] def test_subscribe_to_iterable_empty(self): iterable_finite = [] @@ -46,6 +47,7 @@ def test_subscribe_to_iterable_empty(self): def create(): return rx.from_(iterable_finite) + results = scheduler.start(create) assert results.messages == [on_completed(200)] @@ -58,10 +60,15 @@ def test_double_subscribe_to_iterable(self): results = scheduler.start(lambda: rx.concat(obs, obs)) assert results.messages == [ - on_next(200, 1), on_next(200, 2), on_next(200, 3), - on_next(200, 1), on_next(200, 2), on_next(200, 3), - on_completed(200)] + on_next(200, 1), + on_next(200, 2), + on_next(200, 3), + on_next(200, 1), + on_next(200, 2), + on_next(200, 3), + on_completed(200), + ] def test_observer_throws(self): with self.assertRaises(RxException): - rx.from_iterable([1, 2, 3]).subscribe(lambda x: _raise('ex')) + rx.from_iterable([1, 2, 3]).subscribe(lambda x: _raise("ex")) diff --git a/tests/test_observable/test_observeon.py b/tests/test_observable/test_observeon.py index 13d0e814e..f68996c58 100644 --- a/tests/test_observable/test_observeon.py +++ b/tests/test_observable/test_observeon.py @@ -15,13 +15,10 @@ class TestObserveOn(unittest.TestCase): - def test_observe_on_normal(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), - on_next(210, 2), - on_completed(250) + on_next(150, 1), on_next(210, 2), on_completed(250) ) def create(): @@ -33,11 +30,11 @@ def create(): def test_observe_on_error(self): scheduler = TestScheduler() - ex = 'ex' + ex = "ex" xs = scheduler.create_hot_observable( on_next(150, 1), - on_error(210, ex) + on_error(210, ex), ) def create(): @@ -48,30 +45,28 @@ def create(): assert results.messages == [on_error(210, ex)] assert xs.subscriptions == [subscribe(200, 210)] - def test_observe_on_empty(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( on_next(150, 1), - on_completed(250) + on_completed(250), ) def create(): return xs.pipe(ops.observe_on(scheduler)) + results = scheduler.start(create) assert results.messages == [on_completed(250)] assert xs.subscriptions == [subscribe(200, 250)] - def test_observe_on_never(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable( - on_next(150, 1) - ) + xs = scheduler.create_hot_observable(on_next(150, 1)) def create(): return xs.pipe(ops.observe_on(scheduler)) + results = scheduler.start(create) assert results.messages == [] @@ -91,6 +86,7 @@ def subscribe(observer, scheduler): xs = rx.create(subscribe) xs.pipe(ops.observe_on(scheduler)).subscribe( - scheduler=expected_subscribe_scheduler) + scheduler=expected_subscribe_scheduler + ) assert expected_subscribe_scheduler == actual_subscribe_scheduler diff --git a/tests/test_observable/test_publish.py b/tests/test_observable/test_publish.py index 58de03823..9ddae4712 100644 --- a/tests/test_observable/test_publish.py +++ b/tests/test_observable/test_publish.py @@ -100,7 +100,11 @@ def mapper(ys): def test_ref_count_connects_on_first(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250) + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), ) subject = MySubject() conn = ConnectableObservable(xs, subject) @@ -110,7 +114,13 @@ def create(): res = scheduler.start(create) - assert res.messages == [on_next(210, 1), on_next(220, 2), on_next(230, 3), on_next(240, 4), on_completed(250)] + assert res.messages == [ + on_next(210, 1), + on_next(220, 2), + on_next(230, 3), + on_next(240, 4), + on_completed(250), + ] assert subject.disposed def test_ref_count_notconnected(self): @@ -229,7 +239,11 @@ def action8(scheduler, state): on_next(390, 7), on_next(520, 11), ] - assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 550), subscribe(650, 800)] + assert xs.subscriptions == [ + subscribe(300, 400), + subscribe(500, 550), + subscribe(650, 800), + ] def test_publish_error(self): ex = "ex" @@ -445,7 +459,11 @@ def action8(scheduler, state): scheduler.start() assert results.messages == [on_next(340, 8)] - assert xs.subscriptions == [subscribe(300, 400), subscribe(500, 550), subscribe(650, 800)] + assert xs.subscriptions == [ + subscribe(300, 400), + subscribe(500, 550), + subscribe(650, 800), + ] def test_publish_multipleconnections(self): xs = rx.never() diff --git a/tests/test_observable/test_subscribeon.py b/tests/test_observable/test_subscribeon.py index 102be4620..50467596b 100644 --- a/tests/test_observable/test_subscribeon.py +++ b/tests/test_observable/test_subscribeon.py @@ -13,14 +13,13 @@ created = ReactiveTest.created -class TestSubscribeOn(unittest.TestCase): - +class Testsubscribe_on(unittest.TestCase): def test_subscribe_on_normal(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( on_next(150, 1), on_next(210, 2), - on_completed(250) + on_completed(250), ) def create(): @@ -32,10 +31,10 @@ def create(): def test_subscribe_on_error(self): scheduler = TestScheduler() - ex = 'ex' + ex = "ex" xs = scheduler.create_hot_observable( on_next(150, 1), - on_error(210, ex) + on_error(210, ex), ) def create(): @@ -50,7 +49,7 @@ def test_subscribe_on_empty(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( on_next(150, 1), - on_completed(250) + on_completed(250), ) def create(): @@ -63,9 +62,7 @@ def create(): def test_subscribe_on_never(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable( - on_next(150, 1) - ) + xs = scheduler.create_hot_observable(on_next(150, 1)) def create(): return xs.pipe(ops.subscribe_on(scheduler)) @@ -76,5 +73,5 @@ def create(): assert xs.subscriptions == [subscribe(200, 1000)] -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From 068981135dfdc5b3e0003045e949d4a873b2c5df Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 10 Feb 2022 17:40:13 +0100 Subject: [PATCH 059/103] Fix import sorting --- rx/core/abc/observable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rx/core/abc/observable.py b/rx/core/abc/observable.py index e99b96d32..8565ee538 100644 --- a/rx/core/abc/observable.py +++ b/rx/core/abc/observable.py @@ -2,7 +2,7 @@ from typing import Callable, Generic, Optional, TypeVar, Union from .disposable import DisposableBase -from .observer import ObserverBase, OnNext, OnError, OnCompleted +from .observer import ObserverBase, OnCompleted, OnError, OnNext from .scheduler import SchedulerBase _T_out = TypeVar("_T_out", covariant=True) From bc7937b7c6c9be24fbb86a9b56241a11ee586612 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 06:45:39 +0100 Subject: [PATCH 060/103] Simplify ScheduledDisposable - Refactored from .NET Reactive --- rx/disposable/scheduleddisposable.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/rx/disposable/scheduleddisposable.py b/rx/disposable/scheduleddisposable.py index 81e325d22..b64479862 100644 --- a/rx/disposable/scheduleddisposable.py +++ b/rx/disposable/scheduleddisposable.py @@ -2,6 +2,7 @@ from typing import Any from rx.core import abc +from .singleassignmentdisposable import SingleAssignmentDisposable class ScheduledDisposable(abc.DisposableBase): @@ -15,27 +16,22 @@ def __init__( that uses a Scheduler on which to dispose the disposable.""" self.scheduler = scheduler - self.disposable = disposable - self.is_disposed = False + self.disposable = SingleAssignmentDisposable() + self.disposable.disposable = disposable self.lock = RLock() super().__init__() + @property + def is_disposed(self) -> bool: + return self.disposable.is_disposed + def dispose(self) -> None: """Disposes the wrapped disposable on the provided scheduler.""" - parent = self - def action(scheduler: abc.SchedulerBase, state: Any): """Scheduled dispose action""" - should_dispose = False - - with self.lock: - if not parent.is_disposed: - parent.is_disposed = True - should_dispose = True - if should_dispose: - parent.disposable.dispose() + self.disposable.dispose() self.scheduler.schedule(action) From 20621f0115198a0148c425f8af02da2939201e0b Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 06:52:23 +0100 Subject: [PATCH 061/103] Sort imports --- rx/disposable/scheduleddisposable.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rx/disposable/scheduleddisposable.py b/rx/disposable/scheduleddisposable.py index b64479862..690e969a8 100644 --- a/rx/disposable/scheduleddisposable.py +++ b/rx/disposable/scheduleddisposable.py @@ -2,6 +2,7 @@ from typing import Any from rx.core import abc + from .singleassignmentdisposable import SingleAssignmentDisposable From de4ce44f74356fd68b41bed437c94f2373dd9709 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 07:40:28 +0100 Subject: [PATCH 062/103] Revert rename changes --- notebooks/reactivex.io/Part VII - Meta Operations.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/notebooks/reactivex.io/Part VII - Meta Operations.ipynb b/notebooks/reactivex.io/Part VII - Meta Operations.ipynb index b5da6226a..2ac768fa5 100644 --- a/notebooks/reactivex.io/Part VII - Meta Operations.ipynb +++ b/notebooks/reactivex.io/Part VII - Meta Operations.ipynb @@ -421,7 +421,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# I want an operator to operate on a particular Scheduler: **[subscribe_on](http://reactivex.io/documentation/operators/subscribe_on.html)**\n", + "# I want an operator to operate on a particular Scheduler: **[subscribe_on](http://reactivex.io/documentation/operators/subscribeon.html)**\n", "\n", "Advanced feature: Adding **side effects** to subscription and unsubscription events.\n", "\n", @@ -430,7 +430,7 @@ "![](https://i.imgur.com/CRLzESV.png)\n", "\n", "Plus see the other\n", - "links on [RX docu](http://reactivex.io/documentation/operators/subscribe_on.html)" + "links on [RX docu](http://reactivex.io/documentation/operators/subscribeon.html)" ] }, { @@ -448,7 +448,7 @@ "\n", "========== subscribe_on ==========\n", "\n", - "module rx.linq.observable.subscribe_on\n", + "module rx.linq.observable.subscribeon\n", "@extensionmethod(ObservableBase)\n", "def subscribe_on(self, scheduler):\n", " Subscribe on the specified scheduler.\n", @@ -532,7 +532,7 @@ "collapsed": true }, "source": [ - "## ...when it notifies observers **[observe_on](http://reactivex.io/documentation/operators/subscribe_on.html)**\n", + "## ...when it notifies observers **[observe_on](http://reactivex.io/documentation/operators/subscribeon.html)**\n", "\n", "Via this you can add side effects on any notification to any subscriber.\n", "\n", From c6789f106b86e1bcd3ad1653cabea5ecd29c06d9 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 07:58:28 +0100 Subject: [PATCH 063/103] Revert rename --- tests/test_observable/test_subscribeon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_observable/test_subscribeon.py b/tests/test_observable/test_subscribeon.py index 50467596b..6e6a27d5f 100644 --- a/tests/test_observable/test_subscribeon.py +++ b/tests/test_observable/test_subscribeon.py @@ -13,7 +13,7 @@ created = ReactiveTest.created -class Testsubscribe_on(unittest.TestCase): +class TestSubscribeOn(unittest.TestCase): def test_subscribe_on_normal(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( From 32016443895e545bd43248a7b5b795e8f9bbeed0 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 08:07:14 +0100 Subject: [PATCH 064/103] Fix flaky test --- tests/test_scheduler/test_eventloop/test_asyncioscheduler.py | 2 +- .../test_eventloop/test_asynciothreadsafescheduler.py | 2 +- tests/test_scheduler/test_immediatescheduler.py | 2 +- tests/test_scheduler/test_mainloop/test_tkinterscheduler.py | 5 ++--- tests/test_scheduler/test_trampolinescheduler.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py b/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py index 91c456763..0a4e43465 100644 --- a/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py +++ b/tests/test_scheduler/test_eventloop/test_asyncioscheduler.py @@ -11,7 +11,7 @@ def test_asyncio_schedule_now(self): loop = asyncio.get_event_loop() scheduler = AsyncIOScheduler(loop) diff = scheduler.now - datetime.utcfromtimestamp(loop.time()) - assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may take 1 ms in CI def test_asyncio_schedule_now_units(self): loop = asyncio.get_event_loop() diff --git a/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py b/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py index 4ef4100ba..3be7bfdf8 100644 --- a/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py +++ b/tests/test_scheduler/test_eventloop/test_asynciothreadsafescheduler.py @@ -12,7 +12,7 @@ def test_asyncio_threadsafe_schedule_now(self): loop = asyncio.get_event_loop() scheduler = AsyncIOThreadSafeScheduler(loop) diff = scheduler.now - datetime.utcfromtimestamp(loop.time()) - assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may take 1 ms in CI def test_asyncio_threadsafe_schedule_now_units(self): loop = asyncio.get_event_loop() diff --git a/tests/test_scheduler/test_immediatescheduler.py b/tests/test_scheduler/test_immediatescheduler.py index 6ddef6c38..0008bbaa8 100644 --- a/tests/test_scheduler/test_immediatescheduler.py +++ b/tests/test_scheduler/test_immediatescheduler.py @@ -47,7 +47,7 @@ class MyScheduler(ImmediateScheduler): def test_immediate_now(self): scheduler = ImmediateScheduler() diff = scheduler.now - default_now() - assert abs(diff) <= timedelta(milliseconds=2) # NOTE: may be 1 ms in CI + assert abs(diff) <= timedelta(milliseconds=2) # NOTE: may take 1 ms in CI def test_immediate_now_units(self): scheduler = ImmediateScheduler() diff --git a/tests/test_scheduler/test_mainloop/test_tkinterscheduler.py b/tests/test_scheduler/test_mainloop/test_tkinterscheduler.py index 32681c10d..ba7d3896b 100644 --- a/tests/test_scheduler/test_mainloop/test_tkinterscheduler.py +++ b/tests/test_scheduler/test_mainloop/test_tkinterscheduler.py @@ -12,7 +12,7 @@ try: root = tkinter.Tk() - root.withdraw() # Don't actually draw anything + root.withdraw() # Don't actually draw anything display = True except Exception: display = False @@ -20,11 +20,10 @@ @pytest.mark.skipif("display == False") class TestTkinterScheduler(unittest.TestCase): - def test_tkinter_schedule_now(self): scheduler = TkinterScheduler(root) res = scheduler.now - default_now() - assert abs(res) <= timedelta(milliseconds=1) + assert abs(res) <= timedelta(milliseconds=2) # NOTE: may take 1 ms in CI def test_tkinter_schedule_now_units(self): scheduler = TkinterScheduler(root) diff --git a/tests/test_scheduler/test_trampolinescheduler.py b/tests/test_scheduler/test_trampolinescheduler.py index 2d13c64ef..db43c3c42 100644 --- a/tests/test_scheduler/test_trampolinescheduler.py +++ b/tests/test_scheduler/test_trampolinescheduler.py @@ -12,7 +12,7 @@ class TestTrampolineScheduler(unittest.TestCase): def test_trampoline_now(self): scheduler = TrampolineScheduler() diff = scheduler.now - default_now() - assert abs(diff) < timedelta(milliseconds=2) # NOTE: may be 1 ms in CI + assert abs(diff) < timedelta(milliseconds=2) # NOTE: may take 1 ms in CI def test_trampoline_now_units(self): scheduler = TrampolineScheduler() From 382b95c7456d6e2b7450a64a10dc742730d8de8a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 15:18:20 +0100 Subject: [PATCH 065/103] Remove duplicated settings --- pytest.ini | 2 -- setup.cfg | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index de19c9f0b..000000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -testpaths = tests \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 6c34a36c9..ec0f941ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,7 @@ test = pytest [tool:pytest] testpaths = tests +asyncio_mode = strict [flake8] max-line-length = 88 From 613938c59dde92e56ef8965edf3fd81898e29762 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 11 Feb 2022 15:18:55 +0100 Subject: [PATCH 066/103] Avoid using builtin name for function --- rx/core/observable/zip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index 5f1e2a9ea..14542a237 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -36,7 +36,7 @@ def subscribe( is_completed = [False] * n @synchronized(lock) - def next(i: int): + def next_(i: int): if all(len(q) for q in queues): try: queued_values = [x.pop(0) for x in queues] @@ -74,7 +74,7 @@ def func(i: int): def on_next(x: Any): queues[i].append(x) - next(i) + next_(i) sad.disposable = source.subscribe( on_next, observer.on_error, lambda: completed(i), scheduler=scheduler From 2e98f16d2ae43825569a71f502fc81659d6f5031 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 10:16:22 +0100 Subject: [PATCH 067/103] Type hint fixes --- rx/__init__.py | 13 +++--- rx/core/notification.py | 4 +- rx/core/observable/catch.py | 2 +- rx/core/observer/autodetachobserver.py | 2 +- rx/core/operators/windowwithtimeorcount.py | 50 +++++++++++---------- rx/testing/marbles.py | 51 ++++++++++++++-------- rx/testing/reactivetest.py | 8 ++-- rx/testing/testscheduler.py | 6 +-- tests/test_observable/test_forin.py | 28 ++++++++---- 9 files changed, 96 insertions(+), 68 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 9f472ead5..cb9088121 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -343,7 +343,9 @@ def empty(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]: return empty_(scheduler) -def for_in(values: Iterable[_T1], mapper: typing.Mapper[_T1, _T2]) -> Observable[_T2]: +def for_in( + values: Iterable[_T1], mapper: typing.Mapper[_T1, Observable[_T2]] +) -> Observable[_T2]: """Concatenates the observable sequences obtained by running the specified result mapper for each element in the specified values. @@ -372,7 +374,8 @@ def for_in(values: Iterable[_T1], mapper: typing.Mapper[_T1, _T2]) -> Observable sequences. """ - return concat_with_iterable(map(mapper, values)) + mapped: Iterable[Observable[_T2]] = map(mapper, values) + return concat_with_iterable(mapped) def fork_join(*sources: Observable[_T]) -> Observable[_T]: @@ -520,7 +523,7 @@ def from_marbles( string: str, timespan: typing.RelativeTime = 0.1, scheduler: Optional[abc.SchedulerBase] = None, - lookup: Optional[Mapping[str, Any]] = None, + lookup: Optional[Mapping[Union[str, float], Any]] = None, error: Optional[Exception] = None, ) -> Observable[Any]: """Convert a marble diagram string to a cold observable sequence, using @@ -664,9 +667,9 @@ def hot( timespan: typing.RelativeTime = 0.1, duetime: typing.AbsoluteOrRelativeTime = 0.0, scheduler: Optional[abc.SchedulerBase] = None, - lookup: Optional[Mapping] = None, + lookup: Optional[Mapping[Union[str, float], Any]] = None, error: Optional[Exception] = None, -) -> Observable: +) -> Observable[Any]: """Convert a marble diagram string to a hot observable sequence, using an optional scheduler to enumerate the events. diff --git a/rx/core/notification.py b/rx/core/notification.py index 1597bc31d..b397da646 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -134,11 +134,11 @@ def __str__(self) -> str: class OnError(Notification[_T]): """Represents an OnError notification to an observer.""" - def __init__(self, exception: Exception): + def __init__(self, error: Union[Exception, str]): """Constructs a notification of an exception.""" super(OnError, self).__init__() - self.exception = exception + self.exception = error self.kind = "E" def _accept( diff --git a/rx/core/observable/catch.py b/rx/core/observable/catch.py index 62df51140..3da8ece9a 100644 --- a/rx/core/observable/catch.py +++ b/rx/core/observable/catch.py @@ -42,7 +42,7 @@ def subscribe( last_exception = None is_disposed = False - def action(action1, state: Any = None): + def action(scheduler: abc.SchedulerBase, state: Any = None): def on_error(exn: Exception) -> None: nonlocal last_exception last_exception = exn diff --git a/rx/core/observer/autodetachobserver.py b/rx/core/observer/autodetachobserver.py index 336845d76..9067ccd57 100644 --- a/rx/core/observer/autodetachobserver.py +++ b/rx/core/observer/autodetachobserver.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, TypeVar +from typing import Optional, TypeVar from rx.disposable import SingleAssignmentDisposable from rx.internal import default_error, noop diff --git a/rx/core/operators/windowwithtimeorcount.py b/rx/core/operators/windowwithtimeorcount.py index 831ae3564..12ecf34dc 100644 --- a/rx/core/operators/windowwithtimeorcount.py +++ b/rx/core/operators/windowwithtimeorcount.py @@ -26,59 +26,61 @@ def subscribe( ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - n = [0] - s: List[Optional[Subject[_T]]] = [None] + n: int = 0 + s: Subject[_T] = Subject() timer_d = SerialDisposable() - window_id = [0] + window_id = 0 group_disposable = CompositeDisposable(timer_d) ref_count_disposable = RefCountDisposable(group_disposable) - def create_timer(_id): + def create_timer(_id: int): + nonlocal n, s, window_id m = SingleAssignmentDisposable() timer_d.disposable = m def action(scheduler: abc.SchedulerBase, state: Any = None): - if _id != window_id[0]: + nonlocal n, s, window_id + if _id != window_id: return - n[0] = 0 - window_id[0] += 1 - new_id = window_id[0] - s[0].on_completed() - s[0] = Subject() - observer.on_next(add_ref(s[0], ref_count_disposable)) + n = 0 + window_id += 1 + new_id = window_id + s.on_completed() + s = Subject() + observer.on_next(add_ref(s, ref_count_disposable)) create_timer(new_id) m.disposable = _scheduler.schedule_relative(timespan, action) - s[0] = Subject() - observer.on_next(add_ref(s[0], ref_count_disposable)) + observer.on_next(add_ref(s, ref_count_disposable)) create_timer(0) def on_next(x: _T) -> None: + nonlocal n, s, window_id new_window = False new_id = 0 - s[0].on_next(x) - n[0] += 1 - if n[0] == count: + s.on_next(x) + n += 1 + if n == count: new_window = True - n[0] = 0 - window_id[0] += 1 - new_id = window_id[0] - s[0].on_completed() - s[0] = Subject() - observer.on_next(add_ref(s[0], ref_count_disposable)) + n = 0 + window_id += 1 + new_id = window_id + s.on_completed() + s = Subject() + observer.on_next(add_ref(s, ref_count_disposable)) if new_window: create_timer(new_id) def on_error(e: Exception) -> None: - s[0].on_error(e) + s.on_error(e) observer.on_error(e) def on_completed() -> None: - s[0].on_completed() + s.on_completed() observer.on_completed() group_disposable.add( diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index 4abec988d..e4dd1789b 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -1,6 +1,6 @@ from collections import namedtuple from contextlib import contextmanager -from typing import Dict, List, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union from warnings import warn import rx @@ -9,7 +9,11 @@ from rx.core.observable.marbles import parse from rx.core.typing import Callable, RelativeTime from rx.scheduler import NewThreadScheduler -from rx.testing import ReactiveTest, Recorded, TestScheduler + +from .hotobservable import HotObservable +from .reactivetest import ReactiveTest +from .recorded import Recorded +from .testscheduler import TestScheduler new_thread_scheduler = NewThreadScheduler() @@ -17,7 +21,7 @@ @contextmanager -def marbles_testing(timespan=1.0): +def marbles_testing(timespan: RelativeTime = 1.0): """ Initialize a :class:`rx.testing.TestScheduler` and return a namedtuple containing the following functions that wrap its methods. @@ -77,15 +81,16 @@ def check(): ) def test_start( - create: Union[Observable, Callable[[], Observable]] - ) -> List[Recorded]: + create: Union[Observable[Any], Callable[[], Observable[Any]]] + ) -> List[Recorded[Any]]: nonlocal start_called check() - def default_create(): - return create - if isinstance(create, Observable): + + def default_create() -> Observable[Any]: + return create + create_function = default_create else: create_function = create @@ -100,8 +105,10 @@ def default_create(): return mock_observer.messages def test_expected( - string: str, lookup: Dict = None, error: Exception = None - ) -> List[Recorded]: + string: str, + lookup: Optional[Dict[Union[str, float], Any]] = None, + error: Optional[Exception] = None, + ) -> List[Recorded[Any]]: messages = parse( string, timespan=timespan, @@ -112,8 +119,10 @@ def test_expected( return messages_to_records(messages) def test_cold( - string: str, lookup: Dict = None, error: Exception = None - ) -> Observable: + string: str, + lookup: Optional[Dict[Union[str, float], Any]] = None, + error: Optional[Exception] = None, + ) -> Observable[Any]: check() return rx.from_marbles( string, @@ -123,10 +132,12 @@ def test_cold( ) def test_hot( - string: str, lookup: Dict = None, error: Exception = None - ) -> Observable: + string: str, + lookup: Optional[Dict[Union[str, float], Any]] = None, + error: Optional[Exception] = None, + ) -> Observable[Any]: check() - hot_obs = rx.hot( + hot_obs: HotObservable[Any] = rx.hot( string, timespan=timespan, duetime=subscribed, @@ -143,15 +154,17 @@ def test_hot( def messages_to_records( - messages: List[Tuple[RelativeTime, Notification]] -) -> List[Recorded]: + messages: List[Tuple[RelativeTime, Notification[Any]]] +) -> List[Recorded[Any]]: """ Helper function to convert messages returned by parse() to a list of Recorded. """ - records = [] + records: List[Recorded[Any]] = [] - dispatcher = dict( + dispatcher: Dict[ + str, Callable[[RelativeTime, Notification[Any]], Recorded[Any]] + ] = dict( N=lambda t, n: ReactiveTest.on_next(t, n.value), E=lambda t, n: ReactiveTest.on_error(t, n.exception), C=lambda t, n: ReactiveTest.on_completed(t), diff --git a/rx/testing/reactivetest.py b/rx/testing/reactivetest.py index ae511beb3..3282a0062 100644 --- a/rx/testing/reactivetest.py +++ b/rx/testing/reactivetest.py @@ -67,11 +67,11 @@ def on_next(ticks: int, value: _T) -> Recorded[_T]: return Recorded(ticks, OnNext(value)) @staticmethod - def on_error(ticks: int, exception: Union[Exception, str]) -> Recorded[Any]: - if isinstance(exception, types.FunctionType): - return Recorded(ticks, OnErrorPredicate(exception)) + def on_error(ticks: int, error: Union[Exception, str]) -> Recorded[Any]: + if isinstance(error, types.FunctionType): + return Recorded(ticks, OnErrorPredicate(error)) - return Recorded(ticks, OnError(exception)) + return Recorded(ticks, OnError(error)) @staticmethod def on_completed(ticks: int) -> Recorded[Any]: diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index cb7b88ad8..03d005e2b 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -54,9 +54,9 @@ def add(cls, absolute, relative): def start( self, create: Optional[Callable[[], Observable[_T]]] = None, - created: Optional[int] = None, - subscribed: Optional[int] = None, - disposed: Optional[int] = None, + created: Optional[float] = None, + subscribed: Optional[float] = None, + disposed: Optional[float] = None, ) -> MockObserver[_T]: """Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the diff --git a/tests/test_observable/test_forin.py b/tests/test_observable/test_forin.py index ac540d997..1fa3ff66f 100644 --- a/tests/test_observable/test_forin.py +++ b/tests/test_observable/test_forin.py @@ -1,6 +1,7 @@ import unittest import rx +from rx.core.observable.observable import Observable from rx.testing import TestScheduler, ReactiveTest on_next = ReactiveTest.on_next @@ -13,34 +14,43 @@ class TestForIn(unittest.TestCase): - def test_for_basic(self): scheduler = TestScheduler() def create(): - def mapper(x): + def mapper(x: int) -> Observable[int]: return scheduler.create_cold_observable( on_next(x * 100 + 10, x * 10 + 1), on_next(x * 100 + 20, x * 10 + 2), on_next(x * 100 + 30, x * 10 + 3), - on_completed(x * 100 + 40)) + on_completed(x * 100 + 40), + ) + return rx.for_in([1, 2, 3], mapper) results = scheduler.start(create=create) assert results.messages == [ - on_next(310, 11), on_next(320, 12), on_next(330, 13), - on_next(550, 21), on_next(560, 22), on_next(570, 23), - on_next(890, 31), on_next(900, 32), on_next(910, 33), - on_completed(920) + on_next(310, 11), + on_next(320, 12), + on_next(330, 13), + on_next(550, 21), + on_next(560, 22), + on_next(570, 23), + on_next(890, 31), + on_next(900, 32), + on_next(910, 33), + on_completed(920), ] def test_for_throws(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() def create(): - def mapper(x): + def mapper(x: int): raise Exception(ex) + return rx.for_in([1, 2, 3], mapper) + results = scheduler.start(create=create) assert results.messages == [on_error(200, ex)] From e22a40332516cb38c4fe133157cbfe60bbc9dc1b Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 10:44:07 +0100 Subject: [PATCH 068/103] Fix type hints and circular dependency --- rx/core/notification.py | 8 ++++---- rx/core/observable/onerrorresumenext.py | 2 +- rx/core/run.py | 4 ++-- rx/internal/priorityqueue.py | 4 ++-- rx/testing/marbles.py | 15 ++++++--------- rx/testing/recorded.py | 2 +- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/rx/core/notification.py b/rx/core/notification.py index b397da646..c3cdbe625 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -16,8 +16,8 @@ class Notification(Generic[_T]): def __init__(self) -> None: """Default constructor used by derived types.""" self.has_value = False - self.value = None - self.kind = "" + self.value: Optional[_T] = None + self.kind: str = "" def accept( self, @@ -109,7 +109,7 @@ def __init__(self, value: _T): """Constructs a notification of a new value.""" super(OnNext, self).__init__() - self.value = value + self.value: _T = value self.has_value = True self.kind = "N" @@ -138,7 +138,7 @@ def __init__(self, error: Union[Exception, str]): """Constructs a notification of an exception.""" super(OnError, self).__init__() - self.exception = error + self.exception: Union[Exception, str] = error self.kind = "E" def _accept( diff --git a/rx/core/observable/onerrorresumenext.py b/rx/core/observable/onerrorresumenext.py index 1a8238438..95f36f977 100644 --- a/rx/core/observable/onerrorresumenext.py +++ b/rx/core/observable/onerrorresumenext.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Any, Callable, Optional, TypeVar, Union +from typing import Callable, Optional, TypeVar, Union import rx from rx.core import Observable, abc diff --git a/rx/core/run.py b/rx/core/run.py index 5bd3837b8..84fd0abe7 100644 --- a/rx/core/run.py +++ b/rx/core/run.py @@ -1,5 +1,5 @@ import threading -from typing import Any, Optional, TypeVar, cast +from typing import Optional, TypeVar, cast from rx.internal.exceptions import SequenceContainsNoElementsError from rx.scheduler import NewThreadScheduler @@ -61,7 +61,7 @@ def on_completed() -> None: latch.wait() if exception: - raise exception # pylint: disable=raidfsing-bad-type + raise cast(Exception, exception) if not has_result: raise SequenceContainsNoElementsError diff --git a/rx/internal/priorityqueue.py b/rx/internal/priorityqueue.py index 1d91f7c87..83a4c8ac2 100644 --- a/rx/internal/priorityqueue.py +++ b/rx/internal/priorityqueue.py @@ -14,7 +14,7 @@ def __init__(self) -> None: self.items: List[Tuple[_T1, int]] = [] self.count = PriorityQueue.MIN_COUNT # Monotonic increasing for sort stability - def __len__(self): + def __len__(self) -> int: """Returns length of queue""" return len(self.items) @@ -48,7 +48,7 @@ def remove(self, item: _T1) -> bool: return False - def clear(self): + def clear(self) -> None: """Remove all items from the queue.""" self.items = [] self.count = PriorityQueue.MIN_COUNT diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index e4dd1789b..bb3d9ff03 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -10,7 +10,6 @@ from rx.core.typing import Callable, RelativeTime from rx.scheduler import NewThreadScheduler -from .hotobservable import HotObservable from .reactivetest import ReactiveTest from .recorded import Recorded from .testscheduler import TestScheduler @@ -137,7 +136,7 @@ def test_hot( error: Optional[Exception] = None, ) -> Observable[Any]: check() - hot_obs: HotObservable[Any] = rx.hot( + hot_obs: Observable[Any] = rx.hot( string, timespan=timespan, duetime=subscribed, @@ -162,13 +161,11 @@ def messages_to_records( """ records: List[Recorded[Any]] = [] - dispatcher: Dict[ - str, Callable[[RelativeTime, Notification[Any]], Recorded[Any]] - ] = dict( - N=lambda t, n: ReactiveTest.on_next(t, n.value), - E=lambda t, n: ReactiveTest.on_error(t, n.exception), - C=lambda t, n: ReactiveTest.on_completed(t), - ) + dispatcher: Dict[str, Callable[[int, Notification[Any]], Recorded[Any]]] = { + "N": lambda t, n: ReactiveTest.on_next(t, n.value), + "E": lambda t, n: ReactiveTest.on_error(t, n.exception), + "C": lambda t, n: ReactiveTest.on_completed(t), + } for message in messages: time, notification = message diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index 8aab58817..2b0d0b525 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -4,7 +4,7 @@ from rx.internal.basic import default_comparer if TYPE_CHECKING: - from rx.testing.reactivetest import OnErrorPredicate, OnNextPredicate + from .reactivetest import OnErrorPredicate, OnNextPredicate _T = TypeVar("_T") From 62c57bf868e931663c4529c6cd1e5593313a3ad3 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 16:33:56 +0100 Subject: [PATCH 069/103] Fix typing for pluck, groupjoin,join, catch, buffer* --- rx/core/operators/buffer.py | 27 +++------ rx/core/operators/bufferwithtime.py | 13 +++-- rx/core/operators/bufferwithtimeorcount.py | 19 +++++-- rx/core/operators/catch.py | 4 +- rx/core/operators/groupbyuntil.py | 4 +- rx/core/operators/groupjoin.py | 31 ++++++---- rx/core/operators/join.py | 25 +++++--- rx/core/operators/pluck.py | 19 +++++-- rx/operators/__init__.py | 66 ++++++++++++---------- 9 files changed, 120 insertions(+), 88 deletions(-) diff --git a/rx/core/operators/buffer.py b/rx/core/operators/buffer.py index aff69f33d..ec3deaf49 100644 --- a/rx/core/operators/buffer.py +++ b/rx/core/operators/buffer.py @@ -11,12 +11,7 @@ def buffer_( ) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window(boundaries), - ops.flat_map( - pipe( - ops.to_iterable(), - ops.map(list), - ), - ), + ops.flat_map(ops.to_list()), ) @@ -25,12 +20,7 @@ def buffer_when_( ) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window_when(closing_mapper), - ops.flat_map( - pipe( - ops.to_iterable(), - ops.map(list), - ) - ), + ops.flat_map(ops.to_list()), ) @@ -39,12 +29,7 @@ def buffer_toggle_( ) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window_toggle(openings, closing_mapper), - ops.flat_map( - pipe( - ops.to_iterable(), - ops.map(list), - ) - ), + ops.flat_map(ops.to_list()), ) @@ -75,8 +60,10 @@ def buffer_with_count(source: Observable[_T]) -> Observable[List[_T]]: if skip is None: skip = count - def mapper(value: _T) -> List[_T]: - return value.pipe(ops.to_iterable(), ops.map(list)) + def mapper(value: Observable[_T]) -> Observable[List[_T]]: + return value.pipe( + ops.to_list(), + ) def predicate(value: List[_T]) -> bool: return len(value) > 0 diff --git a/rx/core/operators/bufferwithtime.py b/rx/core/operators/bufferwithtime.py index ddd8cf0f1..0d39d6b1f 100644 --- a/rx/core/operators/bufferwithtime.py +++ b/rx/core/operators/bufferwithtime.py @@ -1,18 +1,23 @@ -from typing import Callable, Optional +from typing import Callable, Optional, TypeVar, List from rx import operators as ops from rx.core import Observable, abc, pipe, typing +_T = TypeVar("_T") -def _buffer_with_time( + +def buffer_with_time_( timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Observable]: +) -> Callable[[Observable[_T]], Observable[List[_T]]]: if not timeshift: timeshift = timespan return pipe( ops.window_with_time(timespan, timeshift, scheduler), - ops.flat_map(lambda x: x.pipe(ops.to_iterable())), + ops.flat_map(ops.to_list()), ) + + +__all__ = ["buffer_with_time_"] diff --git a/rx/core/operators/bufferwithtimeorcount.py b/rx/core/operators/bufferwithtimeorcount.py index b88513137..dd828249f 100644 --- a/rx/core/operators/bufferwithtimeorcount.py +++ b/rx/core/operators/bufferwithtimeorcount.py @@ -1,13 +1,20 @@ -from typing import Callable +from typing import Callable, Optional, TypeVar, List from rx import operators as ops -from rx.core import Observable, pipe +from rx.core import Observable, pipe, typing, abc +_T = TypeVar("_T") -def _buffer_with_time_or_count( - timespan, count, scheduler=None -) -> Callable[[Observable], Observable]: + +def buffer_with_time_or_count_( + timespan: typing.RelativeTime, + count: int, + scheduler: Optional[abc.SchedulerBase] = None, +) -> Callable[[Observable[_T]], Observable[List[_T]]]: return pipe( ops.window_with_time_or_count(timespan, count, scheduler), - ops.flat_map(lambda x: x.pipe(ops.to_iterable())), + ops.flat_map(ops.to_iterable()), ) + + +__all__ = ["buffer_with_time_or_count_"] diff --git a/rx/core/operators/catch.py b/rx/core/operators/catch.py index d453876d6..b8ea2d42a 100644 --- a/rx/core/operators/catch.py +++ b/rx/core/operators/catch.py @@ -40,7 +40,7 @@ def on_error(exception: Exception) -> None: return Observable(subscribe) -def _catch( +def catch_( handler: Union[ Observable[_T], Callable[[Exception, Observable[_T]], Observable[_T]] ] @@ -78,4 +78,4 @@ def catch(source: Observable[_T]) -> Observable[_T]: return catch -__all__ = ["_catch"] +__all__ = ["catch_"] diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index 88075bbfa..675f97b4a 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -20,8 +20,8 @@ def group_by_until_( key_mapper: Mapper[_T, _TKey], element_mapper: Optional[Mapper[_T, _TValue]], - duration_mapper: Callable[[GroupedObservable[_TKey, _T]], Observable[Any]], - subject_mapper: Optional[Callable[[], Subject[_T]]] = None, + duration_mapper: Callable[[GroupedObservable[_TKey, _TValue]], Observable[Any]], + subject_mapper: Optional[Callable[[], Subject[_TValue]]] = None, ) -> Callable[[Observable[_T]], Observable[GroupedObservable[_TKey, _TValue]]]: """Groups the elements of an observable sequence according to a specified key mapper function. A duration mapper function is used diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index 349eeaf24..9a0702d3c 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -1,9 +1,9 @@ import logging from collections import OrderedDict -from typing import Any, Callable +from typing import Any, Callable, Optional, Tuple, TypeVar from rx import operators as ops -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import ( CompositeDisposable, RefCountDisposable, @@ -12,14 +12,17 @@ from rx.internal.utils import add_ref from rx.subject import Subject +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") + log = logging.getLogger("Rx") -def _group_join( - right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], -) -> Callable[[Observable], Observable]: +def group_join_( + right: Observable[_T2], + left_duration_mapper: Callable[[_T1], Observable[Any]], + right_duration_mapper: Callable[[_T2], Observable[Any]], +) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. @@ -40,8 +43,11 @@ def _group_join( def nothing(_): return None - def group_join(left: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def group_join(left: Observable[_T1]) -> Observable[Tuple[_T1, _T2]]: + def subscribe( + observer: abc.ObserverBase[Tuple[_T1, _T2]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: group = CompositeDisposable() rcd = RefCountDisposable(group) left_map = OrderedDict() @@ -91,7 +97,7 @@ def expire(): observer.on_error(e) return - def on_error(error): + def on_error(error: Exception) -> Any: for left_value in left_map.values(): left_value.on_error(error) @@ -101,7 +107,7 @@ def on_error(error): nothing, on_error, expire, scheduler=scheduler ) - def on_error_left(error): + def on_error_left(error: Exception) -> None: for left_value in left_map.values(): left_value.on_error(error) @@ -165,3 +171,6 @@ def on_error_right(error): return Observable(subscribe) return group_join + + +__all__ = ["group_join_"] diff --git a/rx/core/operators/join.py b/rx/core/operators/join.py index 31f57c947..60b06b683 100644 --- a/rx/core/operators/join.py +++ b/rx/core/operators/join.py @@ -1,17 +1,20 @@ from collections import OrderedDict -from typing import Any, Callable +from typing import Any, Callable, Optional, Tuple, TypeVar -from rx.core import Observable +from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable from rx.internal import noop from rx.operators import take +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") -def _join( - right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], -) -> Callable[[Observable], Observable]: + +def join_( + right: Observable[_T2], + left_duration_mapper: Callable[[Any], Observable[Any]], + right_duration_mapper: Callable[[Any], Observable[Any]], +) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: def join(source: Observable) -> Observable: """Correlates the elements of two sequences based on overlapping durations. @@ -27,7 +30,10 @@ def join(source: Observable) -> Observable: left = source - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Tuple[_T1, _T2]], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: group = CompositeDisposable() left_done = False left_map = OrderedDict() @@ -133,3 +139,6 @@ def on_completed_right(): return Observable(subscribe) return join + + +__all__ = ["join_"] diff --git a/rx/core/operators/pluck.py b/rx/core/operators/pluck.py index abb7902a4..a08775bd1 100644 --- a/rx/core/operators/pluck.py +++ b/rx/core/operators/pluck.py @@ -1,10 +1,15 @@ -from typing import Any, Callable +from typing import Any, Callable, Dict, TypeVar from rx import operators as ops from rx.core import Observable +_TKey = TypeVar("_TKey") +_TValue = TypeVar("_TValue") -def _pluck(key: Any) -> Callable[[Observable], Observable]: + +def pluck_( + key: _TKey, +) -> Callable[[Observable[Dict[_TKey, _TValue]]], Observable[_TValue]]: """Retrieves the value of a specified key using dict-like access (as in element[key]) from all elements in the Observable sequence. @@ -16,10 +21,13 @@ def _pluck(key: Any) -> Callable[[Observable], Observable]: To pluck an attribute of each element, use pluck_attr. """ - return ops.map(lambda x: x[key]) + def mapper(x: Dict[_TKey, _TValue]) -> _TValue: + return x[key] + + return ops.map(mapper) -def _pluck_attr(prop: str) -> Callable[[Observable], Observable]: +def pluck_attr_(prop: str) -> Callable[[Observable[Any]], Observable[Any]]: """Retrieves the value of a specified property (using getattr) from all elements in the Observable sequence. @@ -33,3 +41,6 @@ def _pluck_attr(prop: str) -> Callable[[Observable], Observable]: """ return ops.map(lambda x: getattr(x, prop)) + + +__all__ = ["pluck_", "pluck_attr_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 39b30115b..120814996 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -120,7 +120,7 @@ def as_observable() -> Callable[[Observable[_T]], Observable[_T]]: def average( - key_mapper: Optional[Mapper[_T, _TKey]] = None + key_mapper: Optional[Mapper[_T, float]] = None ) -> Callable[[Observable[_T]], Observable[float]]: """The average operator. @@ -217,8 +217,8 @@ def buffer_when( def buffer_toggle( - openings: Observable, closing_mapper: Callable[[Any], Observable] -) -> Callable[[Observable], Observable]: + openings: Observable[Any], closing_mapper: Callable[[Any], Observable[Any]] +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers. @@ -254,7 +254,7 @@ def buffer_toggle( def buffer_with_count( count: int, skip: Optional[int] = None -) -> Callable[[Observable[_T]], Observable[_T]]: +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on element count information. @@ -288,7 +288,7 @@ def buffer_with_time( timespan: typing.RelativeTime, timeshift: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[_T]]: +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into zero or more buffers which are produced based on timing information. @@ -319,16 +319,16 @@ def buffer_with_time( An operator function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.bufferwithtime import _buffer_with_time + from rx.core.operators.bufferwithtime import buffer_with_time_ - return _buffer_with_time(timespan, timeshift, scheduler) + return buffer_with_time_(timespan, timeshift, scheduler) def buffer_with_time_or_count( timespan: typing.RelativeTime, count: int, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[_T]]: +) -> Callable[[Observable[_T]], Observable[List[_T]]]: """Projects each element of an observable sequence into a buffer that is completed when either it's full or a given amount of time has elapsed. @@ -356,9 +356,9 @@ def buffer_with_time_or_count( An operator function that takes an observable source and returns an observable sequence of buffers. """ - from rx.core.operators.bufferwithtimeorcount import _buffer_with_time_or_count + from rx.core.operators.bufferwithtimeorcount import buffer_with_time_or_count_ - return _buffer_with_time_or_count(timespan, count, scheduler) + return buffer_with_time_or_count_(timespan, count, scheduler) def catch( @@ -394,9 +394,9 @@ def catch( followed by the elements of the handler sequence in case an exception occurred. """ - from rx.core.operators.catch import _catch + from rx.core.operators.catch import catch_ - return _catch(handler) + return catch_(handler) def combine_latest( @@ -1496,7 +1496,9 @@ def group_by_until( Examples: >>> group_by_until(lambda x: x.id, None, lambda : rx.never()) >>> group_by_until(lambda x: x.id, lambda x: x.name, lambda grp: rx.never()) - >>> group_by_until(lambda x: x.id, lambda x: x.name, lambda grp: rx.never(), lambda: ReplaySubject()) + >>> group_by_until( + lambda x: x.id, lambda x: x.name, lambda grp: rx.never(), lambda: ReplaySubject() + ) Args: key_mapper: A function to extract the key for each element. @@ -1520,10 +1522,10 @@ def group_by_until( def group_join( - right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], -) -> Callable[[Observable], Observable]: + right: Observable[_T2], + left_duration_mapper: Callable[[_T1], Observable[Any]], + right_duration_mapper: Callable[[_T2], Observable[Any]], +) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. @@ -1551,9 +1553,9 @@ def group_join( a tuple from source elements that have an overlapping duration. """ - from rx.core.operators.groupjoin import _group_join + from rx.core.operators.groupjoin import group_join_ - return _group_join(right, left_duration_mapper, right_duration_mapper) + return group_join_(right, left_duration_mapper, right_duration_mapper) def ignore_elements() -> Callable[[Observable[_T]], Observable[_T]]: @@ -1599,10 +1601,10 @@ def is_empty() -> Callable[[Observable[Any]], Observable[bool]]: def join( - right: Observable, - left_duration_mapper: Callable[[Any], Observable], - right_duration_mapper: Callable[[Any], Observable], -) -> Callable[[Observable], Observable]: + right: Observable[_T2], + left_duration_mapper: Callable[[Any], Observable[Any]], + right_duration_mapper: Callable[[Any], Observable[Any]], +) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: """Correlates the elements of two sequences based on overlapping durations. @@ -1629,9 +1631,9 @@ def join( into a tuple from source elements that have an overlapping duration. """ - from rx.core.operators.join import _join + from rx.core.operators.join import join_ - return _join(right, left_duration_mapper, right_duration_mapper) + return join_(right, left_duration_mapper, right_duration_mapper) def last( @@ -1989,7 +1991,9 @@ def multicast( Examples: >>> res = multicast(observable) - >>> res = multicast(subject_factory=lambda scheduler: Subject(), mapper=lambda x: x) + >>> res = multicast( + subject_factory=lambda scheduler: Subject(), mapper=lambda x: x + ) Args: subject_factory: Factory function to create an intermediate @@ -2168,12 +2172,12 @@ def pluck( An operator function that takes an observable source and returns a new observable sequence of key values. """ - from rx.core.operators.pluck import _pluck + from rx.core.operators.pluck import pluck_ - return _pluck(key) + return pluck_(key) -def pluck_attr(prop: str) -> Callable[[Observable], Observable]: +def pluck_attr(prop: str) -> Callable[[Observable[Any]], Observable[Any]]: """Retrieves the value of a specified property (using getattr) from all elements in the Observable sequence. @@ -2187,9 +2191,9 @@ def pluck_attr(prop: str) -> Callable[[Observable], Observable]: An operator function that takes an observable source and returns a new observable sequence of property values. """ - from rx.core.operators.pluck import _pluck_attr + from rx.core.operators.pluck import pluck_attr_ - return _pluck_attr(prop) + return pluck_attr_(prop) def publish( From b5c52dcb8fa41912f4b8d3109fbf97ca4ee17ab4 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 16:35:30 +0100 Subject: [PATCH 070/103] Sort imports --- rx/core/operators/bufferwithtime.py | 2 +- rx/core/operators/bufferwithtimeorcount.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rx/core/operators/bufferwithtime.py b/rx/core/operators/bufferwithtime.py index 0d39d6b1f..f75054fbc 100644 --- a/rx/core/operators/bufferwithtime.py +++ b/rx/core/operators/bufferwithtime.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, List +from typing import Callable, List, Optional, TypeVar from rx import operators as ops from rx.core import Observable, abc, pipe, typing diff --git a/rx/core/operators/bufferwithtimeorcount.py b/rx/core/operators/bufferwithtimeorcount.py index dd828249f..2194bb64d 100644 --- a/rx/core/operators/bufferwithtimeorcount.py +++ b/rx/core/operators/bufferwithtimeorcount.py @@ -1,7 +1,7 @@ -from typing import Callable, Optional, TypeVar, List +from typing import Callable, List, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, pipe, typing, abc +from rx.core import Observable, abc, pipe, typing _T = TypeVar("_T") From d38052c3301f69dd6514442897838c57171d6352 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 17:42:38 +0100 Subject: [PATCH 071/103] More type fixes --- rx/__init__.py | 56 +++++++++++++++++++++++------ rx/core/observable/fromcallback.py | 22 +++++++----- rx/core/observable/returnvalue.py | 4 +-- rx/core/operators/do.py | 57 +++++++++++++++++++----------- rx/core/operators/join.py | 16 ++++----- rx/core/operators/max.py | 1 - rx/core/operators/min.py | 4 +-- rx/core/operators/publishvalue.py | 18 ++++++---- rx/operators/__init__.py | 13 ++++--- 9 files changed, 126 insertions(+), 65 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index cb9088121..386f717cc 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -378,7 +378,37 @@ def for_in( return concat_with_iterable(mapped) -def fork_join(*sources: Observable[_T]) -> Observable[_T]: +@overload +def fork_join(a: Observable[_A], b: Observable[_B]) -> Observable[Tuple[_A, _B]]: + ... + + +@overload +def fork_join( + a: Observable[_A], b: Observable[_B], c: Observable[_C] +) -> Observable[Tuple[_A, _B, _C]]: + ... + + +@overload +def fork_join( + a: Observable[_A], b: Observable[_B], c: Observable[_C], d: Observable[_D] +) -> Observable[Tuple[_A, _B, _C, _D]]: + ... + + +@overload +def fork_join( + a: Observable[_A], + b: Observable[_B], + c: Observable[_C], + d: Observable[_D], + e: Observable[_E], +) -> Observable[Tuple[_A, _B, _C, _D, _E]]: + ... + + +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. @@ -436,14 +466,15 @@ def from_callable( invoking the given supplier function. """ - from .core.observable.returnvalue import _from_callable + from .core.observable.returnvalue import from_callable_ - return _from_callable(supplier, scheduler) + return from_callable_(supplier, scheduler) def from_callback( - func: Callable, mapper: Optional[typing.Mapper] = None -) -> Callable[[], Observable]: + func: Callable[..., Callable[..., None]], + mapper: Optional[typing.Mapper[Any, Any]] = None, +) -> Callable[[], Observable[Any]]: """Converts a callback function to an observable sequence. Args: @@ -458,9 +489,9 @@ def from_callback( the callback, produces an Observable sequence with a single value of the arguments to the callback as a list. """ - from .core.observable.fromcallback import _from_callback + from .core.observable.fromcallback import from_callback_ - return _from_callback(func, mapper) + return from_callback_(func, mapper) def from_future(future: "Future[_T]") -> Observable[_T]: @@ -611,7 +642,9 @@ def generate_with_relative_time( -1-2-3-4-| Example: - >>> res = rx.generate_with_relative_time(0, lambda x: True, lambda x: x + 1, lambda x: 0.5) + >>> res = rx.generate_with_relative_time( + 0, lambda x: True, lambda x: x + 1, lambda x: 0.5 + ) Args: initial_state: Initial state. @@ -933,10 +966,11 @@ def range( 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`. + 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: [Optional] The scheduler to schedule the values on. If not - specified, the default is to use an instance of + scheduler: [Optional] The scheduler to schedule the values on. + If not specified, the default is to use an instance of :class:`CurrentThreadScheduler `. Returns: diff --git a/rx/core/observable/fromcallback.py b/rx/core/observable/fromcallback.py index 9abe53714..1a02c09a6 100644 --- a/rx/core/observable/fromcallback.py +++ b/rx/core/observable/fromcallback.py @@ -1,13 +1,13 @@ -from typing import Callable, Optional +from typing import Any, Callable, Optional -from rx.core import Observable, abc -from rx.core.typing import Mapper +from rx.core import Observable, abc, typing from rx.disposable import Disposable -def _from_callback( - func: Callable, mapper: Optional[Mapper] = None -) -> Callable[[], Observable]: +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: @@ -22,13 +22,14 @@ def _from_callback( the arguments to the callback as a list. """ - def function(*args): + def function(*args: Any): arguments = list(args) def subscribe( - observer: abc.ObserverBase, scheduler: Optional[abc.SchedulerBase] = None + observer: abc.ObserverBase[Any], + scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - def handler(*args): + def handler(*args: Any): results = list(args) if mapper: try: @@ -53,3 +54,6 @@ def handler(*args): return Observable(subscribe) return function + + +__all__ = ["from_callback_"] diff --git a/rx/core/observable/returnvalue.py b/rx/core/observable/returnvalue.py index 249c95db0..55501006f 100644 --- a/rx/core/observable/returnvalue.py +++ b/rx/core/observable/returnvalue.py @@ -39,7 +39,7 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): return Observable(subscribe) -def _from_callable( +def from_callable_( supplier: Callable[[], _T], scheduler: Optional[abc.SchedulerBase] = None ) -> Observable[_T]: def subscribe( @@ -61,4 +61,4 @@ def action(_: abc.SchedulerBase, __: Any = None): return Observable(subscribe) -__all__ = ["return_value_", "_from_callable"] +__all__ = ["return_value_", "from_callable_"] diff --git a/rx/core/operators/do.py b/rx/core/operators/do.py index c7f94bc35..59e31a720 100644 --- a/rx/core/operators/do.py +++ b/rx/core/operators/do.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar +from typing import Any, Callable, List, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import CompositeDisposable @@ -104,15 +104,17 @@ def do_(observer: abc.ObserverBase[_T]) -> Callable[[Observable[_T]], Observable return do_action_(observer.on_next, observer.on_error, observer.on_completed) -def do_after_next(source, after_next): +def do_after_next(source: Observable[_T], after_next: typing.OnNext[_T]): """Invokes an action with each element after it has been emitted downstream. This can be helpful for debugging, logging, and other side effects. after_next -- Action to invoke on each element after it has been emitted """ - def subscribe(observer, scheduler=None): - def on_next(value): + def subscribe( + observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: + def on_next(value: _T): try: observer.on_next(value) after_next(value) @@ -124,7 +126,7 @@ def on_next(value): return Observable(subscribe) -def do_on_subscribe(source: Observable, on_subscribe): +def do_on_subscribe(source: Observable[Any], on_subscribe: typing.Action): """Invokes an action on subscription. This can be helpful for debugging, logging, and other side effects @@ -134,7 +136,9 @@ def do_on_subscribe(source: Observable, on_subscribe): on_subscribe: Action to invoke on subscription """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: on_subscribe() return source.subscribe( observer.on_next, @@ -146,7 +150,7 @@ def subscribe(observer, scheduler=None): return Observable(subscribe) -def do_on_dispose(source: Observable, on_dispose): +def do_on_dispose(source: Observable[Any], on_dispose: typing.Action): """Invokes an action on disposal. This can be helpful for debugging, logging, and other side effects @@ -156,11 +160,13 @@ def do_on_dispose(source: Observable, on_dispose): on_dispose: Action to invoke on disposal """ - class OnDispose(Disposable): + class OnDispose(abc.DisposableBase): def dispose(self) -> None: on_dispose() - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose()) subscription = source.subscribe( @@ -175,7 +181,7 @@ def subscribe(observer, scheduler=None): return Observable(subscribe) -def do_on_terminate(source, on_terminate): +def do_on_terminate(source: Observable[Any], on_terminate: typing.Action): """Invokes an action on an on_complete() or on_error() event. This can be helpful for debugging, logging, and other side effects when completion or an error terminates an operation. @@ -184,7 +190,9 @@ def do_on_terminate(source, on_terminate): on_terminate -- Action to invoke when on_complete or throw is called """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: def on_completed(): try: on_terminate() @@ -193,7 +201,7 @@ def on_completed(): else: observer.on_completed() - def on_error(exception): + def on_error(exception: Exception): try: on_terminate() except Exception as err: # pylint: disable=broad-except @@ -208,7 +216,7 @@ def on_error(exception): return Observable(subscribe) -def do_after_terminate(source, after_terminate): +def do_after_terminate(source: Observable[Any], after_terminate: typing.Action): """Invokes an action after an on_complete() or on_error() event. This can be helpful for debugging, logging, and other side effects when completion or an error terminates an operation @@ -217,7 +225,9 @@ def do_after_terminate(source, after_terminate): on_terminate -- Action to invoke after on_complete or throw is called """ - def subscribe(observer, scheduler=None): + def subscribe( + observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None + ) -> abc.DisposableBase: def on_completed(): observer.on_completed() try: @@ -225,7 +235,7 @@ def on_completed(): except Exception as err: # pylint: disable=broad-except observer.on_error(err) - def on_error(exception): + def on_error(exception: Exception) -> None: observer.on_error(exception) try: after_terminate() @@ -239,7 +249,9 @@ def on_error(exception): return Observable(subscribe) -def do_finally(finally_action: Callable) -> Callable[[Observable], Observable]: +def do_finally( + finally_action: typing.Action, +) -> Callable[[Observable[_T]], Observable[_T]]: """Invokes an action after an on_complete(), on_error(), or disposal event occurs. @@ -254,8 +266,8 @@ def do_finally(finally_action: Callable) -> Callable[[Observable], Observable]: or disposal is called """ - class OnDispose(Disposable): - def __init__(self, was_invoked): + class OnDispose(abc.DisposableBase): + def __init__(self, was_invoked: List[bool]): self.was_invoked = was_invoked def dispose(self) -> None: @@ -263,8 +275,11 @@ def dispose(self) -> None: finally_action() self.was_invoked[0] = True - def partial(source: Observable) -> Observable: - def subscribe(observer, scheduler=None): + def partial(source: Observable[_T]) -> Observable[_T]: + def subscribe( + observer: abc.ObserverBase[_T], + scheduler: Optional[abc.SchedulerBase] = None, + ) -> abc.DisposableBase: was_invoked = [False] @@ -277,7 +292,7 @@ def on_completed(): except Exception as err: # pylint: disable=broad-except observer.on_error(err) - def on_error(exception): + def on_error(exception: Exception): observer.on_error(exception) try: if not was_invoked[0]: diff --git a/rx/core/operators/join.py b/rx/core/operators/join.py index 60b06b683..ef4b95fda 100644 --- a/rx/core/operators/join.py +++ b/rx/core/operators/join.py @@ -15,7 +15,7 @@ def join_( left_duration_mapper: Callable[[Any], Observable[Any]], right_duration_mapper: Callable[[Any], Observable[Any]], ) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: - def join(source: Observable) -> Observable: + def join(source: Observable[_T1]) -> Observable[Tuple[_T1, _T2]]: """Correlates the elements of two sequences based on overlapping durations. @@ -36,13 +36,13 @@ def subscribe( ) -> abc.DisposableBase: group = CompositeDisposable() left_done = False - left_map = OrderedDict() + left_map: OrderedDict[int, _T1] = OrderedDict() left_id = 0 right_done = False - right_map = OrderedDict() + right_map: OrderedDict[int, _T2] = OrderedDict() right_id = 0 - def on_next_left(value): + def on_next_left(value: _T1): nonlocal left_id duration = None current_id = left_id @@ -58,7 +58,7 @@ def expire(): if not len(left_map) and left_done: observer.on_completed() - return group.remove(md) + group.remove(md) try: duration = left_duration_mapper(value) @@ -74,7 +74,7 @@ def expire(): result = (value, val) observer.on_next(result) - def on_completed_left(): + def on_completed_left() -> None: nonlocal left_done left_done = True if right_done or not len(left_map): @@ -89,7 +89,7 @@ def on_completed_left(): ) ) - def on_next_right(value): + def on_next_right(value: _T2): nonlocal right_id duration = None current_id = right_id @@ -104,7 +104,7 @@ def expire(): if not len(right_map) and right_done: observer.on_completed() - return group.remove(md) + group.remove(md) try: duration = right_duration_mapper(value) diff --git a/rx/core/operators/max.py b/rx/core/operators/max.py index 23e71aeca..32f548ba7 100644 --- a/rx/core/operators/max.py +++ b/rx/core/operators/max.py @@ -28,7 +28,6 @@ def max_( an observable sequence containing a single element with the maximum element in the source sequence. """ - return pipe( ops.max_by(identity, comparer), ops.map(first_only), diff --git a/rx/core/operators/min.py b/rx/core/operators/min.py index 7f94ff1ca..b653f2659 100644 --- a/rx/core/operators/min.py +++ b/rx/core/operators/min.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Sequence, TypeVar +from typing import Callable, List, Optional, TypeVar from rx import operators as ops from rx.core import Observable, pipe @@ -9,7 +9,7 @@ _T = TypeVar("_T") -def first_only(x: Sequence[_T]) -> _T: +def first_only(x: List[_T]) -> _T: if not x: raise SequenceContainsNoElementsError() diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index 91c63027f..7f2caf95e 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -1,18 +1,24 @@ -from typing import Any, Callable, Optional +from typing import Callable, Optional, TypeVar from rx import operators as ops -from rx.core import Observable +from rx.core import Observable, abc from rx.core.typing import Mapper from rx.subject import BehaviorSubject +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") -def _publish_value( - initial_value: Any, mapper: Optional[Mapper] = None -) -> Callable[[Observable], Observable]: + +def publish_value_( + initial_value: _T1, mapper: Optional[Mapper[_T1, _T2]] = None +) -> Callable[[Observable[_T1]], Observable[_T2]]: if mapper: - def subject_factory(scheduler): + def subject_factory(scheduler: abc.SchedulerBase) -> BehaviorSubject[_T1]: return BehaviorSubject(initial_value) return ops.multicast(subject_factory=subject_factory, mapper=mapper) return ops.multicast(BehaviorSubject(initial_value)) + + +__all__ = ["publish_value_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 120814996..8a2e5df05 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1497,7 +1497,10 @@ def group_by_until( >>> group_by_until(lambda x: x.id, None, lambda : rx.never()) >>> group_by_until(lambda x: x.id, lambda x: x.name, lambda grp: rx.never()) >>> group_by_until( - lambda x: x.id, lambda x: x.name, lambda grp: rx.never(), lambda: ReplaySubject() + lambda x: x.id, + lambda x: x.name, + lambda grp: rx.never(), + lambda: ReplaySubject() ) Args: @@ -2261,9 +2264,9 @@ def publish_value( sequence produced by multicasting the source sequence within a mapper function. """ - from rx.core.operators.publishvalue import _publish_value + from rx.core.operators.publishvalue import publish_value_ - return _publish_value(initial_value, mapper) + return publish_value_(initial_value, mapper) def reduce( @@ -2348,11 +2351,11 @@ def repeat( def replay( - mapper: Optional[Mapper] = None, + mapper: Optional[Mapper[_T1, _T2]] = None, buffer_size: Optional[int] = None, window: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable], Union[Observable, ConnectableObservable]]: +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T2]]]: """The `replay` operator. Returns an observable sequence that is the result of invoking the From 2899fce12bdecf5995c5a502a86940f66bf2b1e9 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 18:20:17 +0100 Subject: [PATCH 072/103] Typing fixes --- rx/__init__.py | 22 +++++++++++----------- rx/core/operators/debounce.py | 6 +++--- rx/core/operators/publish.py | 6 +++--- rx/core/operators/reduce.py | 9 +++++---- rx/core/operators/skipuntil.py | 9 ++++----- rx/operators/__init__.py | 18 ++++++++++-------- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 386f717cc..1c0e4760a 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -201,7 +201,7 @@ def combine_latest( ... -def combine_latest(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: +def combine_latest(*__sources: Observable[Any]) -> Observable[Any]: """Merges the specified observable sequences into one observable sequence by creating a tuple whenever any of the observable sequences emits an element. @@ -227,7 +227,7 @@ def combine_latest(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: from .core.observable.combinelatest import combine_latest_ - return combine_latest_(*sources) + return combine_latest_(*__sources) def concat(*sources: Observable[_T]) -> Observable[_T]: @@ -379,36 +379,36 @@ def for_in( @overload -def fork_join(a: Observable[_A], b: Observable[_B]) -> Observable[Tuple[_A, _B]]: +def fork_join(__a: Observable[_A], __b: Observable[_B]) -> Observable[Tuple[_A, _B]]: ... @overload def fork_join( - a: Observable[_A], b: Observable[_B], c: Observable[_C] + __a: Observable[_A], __b: Observable[_B], __c: Observable[_C] ) -> Observable[Tuple[_A, _B, _C]]: ... @overload def fork_join( - a: Observable[_A], b: Observable[_B], c: Observable[_C], d: Observable[_D] + __a: Observable[_A], __b: Observable[_B], __c: Observable[_C], __d: Observable[_D] ) -> Observable[Tuple[_A, _B, _C, _D]]: ... @overload def fork_join( - a: Observable[_A], - b: Observable[_B], - c: Observable[_C], - d: Observable[_D], - e: Observable[_E], + __a: Observable[_A], + __b: Observable[_B], + __c: Observable[_C], + __d: Observable[_D], + __e: Observable[_E], ) -> Observable[Tuple[_A, _B, _C, _D, _E]]: ... -def fork_join(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]: +def fork_join(*sources: Observable[Any]) -> Observable[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. diff --git a/rx/core/operators/debounce.py b/rx/core/operators/debounce.py index 68da5c50f..fb40c0b24 100644 --- a/rx/core/operators/debounce.py +++ b/rx/core/operators/debounce.py @@ -11,7 +11,7 @@ _T = TypeVar("_T") -def debounce( +def debounce_( duetime: typing.RelativeTime, scheduler: Optional[abc.SchedulerBase] ) -> Callable[[Observable[_T]], Observable[_T]]: def debounce(source: Observable[_T]) -> Observable[_T]: @@ -79,7 +79,7 @@ def on_completed() -> None: return debounce -def throttle_with_mapper( +def throttle_with_mapper_( throttle_duration_mapper: Callable[[Any], Observable[Any]] ) -> Callable[[Observable[_T]], Observable[_T]]: def throttle_with_mapper(source: Observable[_T]) -> Observable[_T]: @@ -171,4 +171,4 @@ def on_completed() -> None: return throttle_with_mapper -__all__ = ["debounce", "throttle_with_mapper"] +__all__ = ["debounce_", "throttle_with_mapper_"] diff --git a/rx/core/operators/publish.py b/rx/core/operators/publish.py index 0dcd58bd7..3fb78d777 100644 --- a/rx/core/operators/publish.py +++ b/rx/core/operators/publish.py @@ -6,12 +6,12 @@ from rx.subject import Subject _T = TypeVar("_T") -_TResult = TypeVar("_TResult") +_T2 = TypeVar("_T2") def publish_( - mapper: Optional[Mapper[_T, _TResult]] = None, -) -> Callable[[Observable[_T]], ConnectableObservable[_TResult]]: + mapper: Optional[Mapper[_T, _T2]] = None, +) -> Callable[[Observable[_T]], ConnectableObservable[_T2]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence. This operator is a diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 98d1fbc54..953d33674 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, TypeVar +from typing import Any, Callable, Type, TypeVar, Union, cast from rx import operators as ops from rx.core import Observable, pipe @@ -10,7 +10,7 @@ def reduce_( - accumulator: Accumulator[_TState, _T], seed: Any = NotSet + accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single element in the @@ -35,8 +35,9 @@ def reduce_( final accumulator value. """ if seed is not NotSet: - scanner = ops.scan(accumulator, seed=seed) - return pipe(scanner, ops.last_or_default(default_value=seed)) + seed_: _TState = cast(_TState, seed) + scanner = ops.scan(accumulator, seed=seed_) + return pipe(scanner, ops.last_or_default(default_value=seed_)) return pipe(ops.scan(accumulator), ops.last()) diff --git a/rx/core/operators/skipuntil.py b/rx/core/operators/skipuntil.py index eaf6c9c89..b185d6572 100644 --- a/rx/core/operators/skipuntil.py +++ b/rx/core/operators/skipuntil.py @@ -1,16 +1,15 @@ from asyncio import Future -from typing import Any, Callable, Optional, TypeVar, Union, cast +from typing import Any, Callable, Optional, TypeVar, Union from rx import from_future from rx.core import Observable, abc from rx.disposable import CompositeDisposable, SingleAssignmentDisposable -from rx.internal.utils import is_future _T = TypeVar("_T") def skip_until_( - other: Union[Observable[_T], "Future[_T]"] + other: Union[Observable[Any], "Future[Any]"] ) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after the other observable sequence produces a value. @@ -25,10 +24,10 @@ def skip_until_( propagation. """ - if is_future(other): + if isinstance(other, Future): obs: Observable[Any] = from_future(other) else: - obs = cast(Observable[_T], other) + obs = other def skip_until(source: Observable[_T]) -> Observable[_T]: def subscribe( diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 8a2e5df05..c32521ea6 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -543,9 +543,9 @@ def debounce( An operator function that takes the source observable and returns the debounced observable sequence. """ - from rx.core.operators.debounce import debounce + from rx.core.operators.debounce import debounce_ - return debounce(duetime, scheduler) + return debounce_(duetime, scheduler) throttle_with_timeout = debounce @@ -2201,7 +2201,7 @@ def pluck_attr(prop: str) -> Callable[[Observable[Any]], Observable[Any]]: def publish( mapper: Optional[Mapper[_T1, _T2]] = None, -) -> Callable[[Observable[_T2]], ConnectableObservable[_T2]]: +) -> Callable[[Observable[_T1]], ConnectableObservable[_T2]]: """The `publish` operator. Returns an observable sequence that is the result of invoking the @@ -2270,7 +2270,7 @@ def publish_value( def reduce( - accumulator: Accumulator[_TState, _T], seed: Union[_TState, NotSet] = NotSet + accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: """The reduce operator. @@ -2715,7 +2715,9 @@ def skip_last_with_time( return skip_last_with_time_(duration, scheduler=scheduler) -def skip_until(other: Observable[Any]) -> Callable[[Observable[_T]], Observable[_T]]: +def skip_until( + other: Union[Observable[Any], "Future[Any]"] +) -> Callable[[Observable[_T]], Observable[_T]]: """Returns the values from the source observable sequence only after the other observable sequence produces a value. @@ -2981,7 +2983,7 @@ def starmap( def starmap( mapper: Optional[Callable[..., Any]] = None -) -> Callable[[Observable[Tuple[Any, ...]]], Observable[Any]]: +) -> Callable[[Observable[Any]], Observable[Any]]: """The starmap operator. Unpack arguments grouped as tuple elements of an observable @@ -3523,9 +3525,9 @@ def throttle_with_mapper( A partially applied operator function that takes an observable source and returns the throttled observable sequence. """ - from rx.core.operators.debounce import throttle_with_mapper + from rx.core.operators.debounce import throttle_with_mapper_ - return throttle_with_mapper(throttle_duration_mapper) + return throttle_with_mapper_(throttle_duration_mapper) if TYPE_CHECKING: From b672820ecbdb350dad98dcf394be28425b13a5d3 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 19:11:24 +0100 Subject: [PATCH 073/103] Add mypy to CI (partially) --- .github/workflows/pythonpackage.yml | 3 +++ requirements.txt | 4 +++- rx/core/abc/subject.py | 8 +++++--- rx/core/operators/tomarbles.py | 2 +- rx/operators/__init__.py | 30 ++++++++++++++++++----------- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 7b249cc20..22f50d745 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -23,6 +23,9 @@ jobs: run: | black --check --verbose rx isort --check rx + - name: Check type annotations (mypy) + run: | + mypy rx/__init__.py rx/operators/__init__.py rx/core/abc rc/core/observer rx/disposable rx/internal - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/requirements.txt b/requirements.txt index 22375d981..e477d792d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ pytest pytest-xdist +pyright coveralls flake8 black -isort \ No newline at end of file +isort +mypy \ No newline at end of file diff --git a/rx/core/abc/subject.py b/rx/core/abc/subject.py index 954fde9b6..3a21b862e 100644 --- a/rx/core/abc/subject.py +++ b/rx/core/abc/subject.py @@ -1,9 +1,9 @@ from abc import abstractmethod -from typing import Optional, TypeVar +from typing import Optional, TypeVar, Union from .disposable import DisposableBase from .observable import ObservableBase -from .observer import ObserverBase +from .observer import ObserverBase, OnCompleted, OnError, OnNext from .scheduler import SchedulerBase _T = TypeVar("_T") @@ -21,7 +21,9 @@ class SubjectBase(ObserverBase[_T], ObservableBase[_T]): @abstractmethod def subscribe( self, - observer: Optional[ObserverBase[_T]] = None, + on_next: Optional[Union[OnNext[_T], ObserverBase[_T]]] = None, + on_error: Optional[OnError] = None, + on_completed: Optional[OnCompleted] = None, *, scheduler: Optional[SchedulerBase] = None ) -> DisposableBase: diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index d02aa1825..97df06a53 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -8,7 +8,7 @@ def to_marbles( - scheduler: Optional[abc.SchedulerBase] = None, timespan: RelativeTime = 0.1 + timespan: RelativeTime = 0.1, scheduler: Optional[abc.SchedulerBase] = None ): def to_marbles(source: Observable[Any]) -> Observable[str]: """Convert an observable sequence into a marble diagram string. diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index c32521ea6..7c56c77d3 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2962,21 +2962,21 @@ def some( @overload def starmap( - mapper: Optional[Callable[[_A, _B], _T]] = None + mapper: Callable[[_A, _B], _T] ) -> Callable[[Observable[Tuple[_A, _B]]], Observable[_T]]: ... @overload def starmap( - mapper: Optional[Callable[[_A, _B, _C], _T]] = None + mapper: Callable[[_A, _B, _C], _T] ) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: ... @overload def starmap( - mapper: Optional[Callable[[_A, _B, _C, _D], _T]] = None + mapper: Callable[[_A, _B, _C, _D], _T] ) -> Callable[[Observable[Tuple[_A, _B, _C, _D]]], Observable[_T]]: ... @@ -3018,33 +3018,37 @@ def starmap( if mapper is None: return pipe(identity) - return pipe(map(lambda values: mapper(*values))) + def starred(values: Tuple[Any, ...]) -> Any: + assert mapper # mypy is paranoid + return mapper(*values) + + return pipe(map(starred)) @overload def starmap_indexed( - mapper: Optional[Callable[[_A, _B, int], _T]] = None -) -> Callable[[Observable[Tuple[_A, _B]]], Observable[_T]]: + mapper: Callable[[_A, _B, _C, int], _T] +) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: ... @overload def starmap_indexed( - mapper: Optional[Callable[[_A, _B, _C, int], _T]] = None -) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: + mapper: Callable[[_A, _B, int], _T] +) -> Callable[[Observable[Tuple[_A, _B]]], Observable[_T]]: ... @overload def starmap_indexed( - mapper: Optional[Callable[[_A, _B, _C, _D, int], _T]] = None + mapper: Callable[[_A, _B, _C, _D, int], _T] ) -> Callable[[Observable[Tuple[_A, _B, _C, _D]]], Observable[_T]]: ... def starmap_indexed( mapper: Optional[Callable[..., Any]] = None -) -> Callable[[Observable[Tuple[Any, ...]]], Observable[Any]]: +) -> Callable[[Observable[Any]], Observable[Any]]: """Variant of :func:`starmap` which accepts an indexed mapper. .. marble:: @@ -3071,7 +3075,11 @@ def starmap_indexed( if mapper is None: return pipe(identity) - return pipe(map(lambda values: mapper(*values))) + def starred(values: Tuple[Any, ...]) -> Any: + assert mapper # mypy is paranoid + return mapper(*values) + + return pipe(map(starred)) def start_with(*args: _T) -> Callable[[Observable[_T]], Observable[_T]]: From 0fbf66d6dfaefbc800ec1f55aac7ea0cee0b457e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 19:14:36 +0100 Subject: [PATCH 074/103] Fix typo in CI action --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 22f50d745..8a3a0ea74 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -25,7 +25,7 @@ jobs: isort --check rx - name: Check type annotations (mypy) run: | - mypy rx/__init__.py rx/operators/__init__.py rx/core/abc rc/core/observer rx/disposable rx/internal + mypy rx/__init__.py rx/operators/__init__.py rx/core/abc rx/core/observer rx/disposable rx/internal - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names From e6ff2b9980ddd3125d4a280c3c6c87989dd107f6 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 19:22:57 +0100 Subject: [PATCH 075/103] Add mypy options to setup --- setup.cfg | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ec0f941ea..8946e6136 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,4 +13,9 @@ asyncio_mode = strict max-line-length = 88 [isort] -profile = black \ No newline at end of file +profile = black +src_paths=rx + +[mypy] +python_version = 3.9 +follow_imports = silent From 6de69acf2ff8c22f97e9415feeb10ecfd09eb8b3 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 22:08:19 +0100 Subject: [PATCH 076/103] Fixed rx/core/observable --- .github/workflows/pythonpackage.yml | 5 ++-- rx/core/observable/connectableobservable.py | 2 +- rx/core/observable/fromfuture.py | 10 +++---- rx/core/observable/ifthen.py | 12 ++++---- rx/core/observable/marbles.py | 8 +++--- rx/core/observable/repeat.py | 4 +-- rx/core/observable/throw.py | 4 +-- rx/core/observable/timer.py | 31 ++++++++++----------- rx/core/observable/using.py | 2 +- rx/core/observable/zip.py | 8 +++--- rx/scheduler/catchscheduler.py | 2 +- rx/scheduler/historicalscheduler.py | 5 ++-- rx/scheduler/virtualtimescheduler.py | 11 +++++--- tests/test_core/test_notification.py | 16 +++++++---- tests/test_observable/test_fromfuture.py | 5 ++-- 15 files changed, 65 insertions(+), 60 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 8a3a0ea74..8ee952bc8 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -23,9 +23,10 @@ jobs: run: | black --check --verbose rx isort --check rx - - name: Check type annotations (mypy) + - name: Check type annotations (mypy & pyright) run: | - mypy rx/__init__.py rx/operators/__init__.py rx/core/abc rx/core/observer rx/disposable rx/internal + mypy rx/__init__.py rx/operators rx/core/abc rx/core/observer rx/core/observable rx/disposable rx/internal rx/subject rx/scheduler + pyright rx/__init__.py rx/operators rx/core/abc rx/core/observer rx/core/observable rx/disposable rx/internal rx/subject rx/scheduler - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/rx/core/observable/connectableobservable.py b/rx/core/observable/connectableobservable.py index e724f797b..a23b2799f 100644 --- a/rx/core/observable/connectableobservable.py +++ b/rx/core/observable/connectableobservable.py @@ -15,7 +15,7 @@ class ConnectableObservable(Observable[_T]): def __init__(self, source: abc.ObservableBase[_T], subject: abc.SubjectBase[_T]): self.subject = subject self.has_subscription = False - self.subscription = None + self.subscription: Optional[abc.DisposableBase] = None self.source = source super().__init__() diff --git a/rx/core/observable/fromfuture.py b/rx/core/observable/fromfuture.py index 55c0feada..772170e54 100644 --- a/rx/core/observable/fromfuture.py +++ b/rx/core/observable/fromfuture.py @@ -1,6 +1,6 @@ import asyncio from asyncio import Future -from typing import Any, Optional, TypeVar +from typing import Any, Optional, TypeVar, cast from rx.core import Observable, abc from rx.disposable import Disposable @@ -26,11 +26,11 @@ def subscribe( def done(future: "Future[_T]"): try: value: Any = future.result() - except ( - Exception, - asyncio.CancelledError, - ) as ex: # pylint: disable=broad-except + 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() diff --git a/rx/core/observable/ifthen.py b/rx/core/observable/ifthen.py index 9332b853e..9f8cd67e5 100644 --- a/rx/core/observable/ifthen.py +++ b/rx/core/observable/ifthen.py @@ -33,17 +33,19 @@ def if_then_( else_source. """ - else_source = else_source or rx.empty() + else_source_: Union[Observable[_T], "Future[_T]"] = else_source or rx.empty() then_source = ( rx.from_future(then_source) if isinstance(then_source, Future) else then_source ) - else_source = ( - rx.from_future(else_source) if isinstance(else_source, Future) else else_source + else_source_ = ( + rx.from_future(else_source_) + if isinstance(else_source_, Future) + else else_source_ ) - def factory(_: abc.SchedulerBase) -> Observable[_T]: - return then_source if condition() else else_source + def factory(_: abc.SchedulerBase) -> Union[Observable[_T], "Future[_T]"]: + return then_source if condition() else else_source_ return rx.defer(factory) diff --git a/rx/core/observable/marbles.py b/rx/core/observable/marbles.py index 5daff1403..68b3d1498 100644 --- a/rx/core/observable/marbles.py +++ b/rx/core/observable/marbles.py @@ -201,8 +201,8 @@ def parse( """ - error = error or Exception("error") - lookup = lookup or {} + error_ = error or Exception("error") + lookup_ = lookup or {} if isinstance(timespan, timedelta): timespan = timespan.total_seconds() @@ -227,10 +227,10 @@ def map_element( if element == "|": return (time, notification.OnCompleted()) elif element == "#": - return (time, notification.OnError(error)) + return (time, notification.OnError(error_)) else: value = try_number(element) - value = lookup.get(value, value) + value = lookup_.get(value, value) return (time, notification.OnNext(value)) is_stopped = False diff --git a/rx/core/observable/repeat.py b/rx/core/observable/repeat.py index 81e8694bd..3328a3edc 100644 --- a/rx/core/observable/repeat.py +++ b/rx/core/observable/repeat.py @@ -7,9 +7,7 @@ _T = TypeVar("_T") -def repeat_value_( - value: _T = None, repeat_count: Optional[int] = None -) -> Observable[_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. diff --git a/rx/core/observable/throw.py b/rx/core/observable/throw.py index e9ad9e1b4..979d11b90 100644 --- a/rx/core/observable/throw.py +++ b/rx/core/observable/throw.py @@ -7,7 +7,7 @@ def throw_( exception: Union[str, Exception], scheduler: Optional[abc.SchedulerBase] = None ) -> Observable[Any]: - exception = exception if isinstance(exception, Exception) else Exception(exception) + exception_ = exception if isinstance(exception, Exception) else Exception(exception) def subscribe( observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None @@ -15,7 +15,7 @@ def subscribe( _scheduler = scheduler or ImmediateScheduler.singleton() def action(scheduler: abc.SchedulerBase, state: Any): - observer.on_error(exception) + observer.on_error(exception_) return _scheduler.schedule(action) diff --git a/rx/core/observable/timer.py b/rx/core/observable/timer.py index 0e95a5475..3e8745155 100644 --- a/rx/core/observable/timer.py +++ b/rx/core/observable/timer.py @@ -4,17 +4,14 @@ from rx.core import Observable, abc, typing from rx.disposable import MultipleAssignmentDisposable from rx.scheduler import TimeoutScheduler +from rx.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 - ): - _scheduler: abc.SchedulerBase = ( - scheduler or scheduler_ or TimeoutScheduler.singleton() - ) + def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): + _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() def action(scheduler: abc.SchedulerBase, state: Any): observer.on_next(0) @@ -26,13 +23,11 @@ def action(scheduler: abc.SchedulerBase, state: Any): def observable_timer_duetime_and_period( - duetime: typing.AbsoluteTime, + duetime: typing.AbsoluteOrRelativeTime, period: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable[int]: - def subscribe( - observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None - ): + def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() nonlocal duetime @@ -67,9 +62,7 @@ def action(scheduler: abc.SchedulerBase, state: Any): 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 - ): + def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() d = _scheduler.to_seconds(duetime) @@ -95,12 +88,16 @@ def subscribe( observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() + _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() - def action(count: int): - observer.on_next(count) - return count + 1 + 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) diff --git a/rx/core/observable/using.py b/rx/core/observable/using.py index 762c1b9d0..bc636c3ea 100644 --- a/rx/core/observable/using.py +++ b/rx/core/observable/using.py @@ -31,7 +31,7 @@ def using_( def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None ) -> abc.DisposableBase: - disp = Disposable() + disp: abc.DisposableBase = Disposable() try: resource = resource_factory() diff --git a/rx/core/observable/zip.py b/rx/core/observable/zip.py index 14542a237..03488dcac 100644 --- a/rx/core/observable/zip.py +++ b/rx/core/observable/zip.py @@ -66,11 +66,11 @@ def completed(i: int) -> None: subscriptions: List[Optional[abc.DisposableBase]] = [None] * n def func(i: int): - source = sources[i] + source: Observable[Any] = sources[i] + if isinstance(source, Future): + source = from_future(source) + sad = SingleAssignmentDisposable() - source: Observable[Any] = ( - from_future(source) if isinstance(source, Future) else source - ) def on_next(x: Any): queues[i].append(x) diff --git a/rx/scheduler/catchscheduler.py b/rx/scheduler/catchscheduler.py index fa0a389a3..92de32dda 100644 --- a/rx/scheduler/catchscheduler.py +++ b/rx/scheduler/catchscheduler.py @@ -155,7 +155,7 @@ def _wrap( parent = self def wrapped_action( - self: CatchScheduler, state: Optional[_TState] + self: abc.SchedulerBase, state: Optional[_TState] ) -> Optional[abc.DisposableBase]: try: return action(parent._get_recursive_wrapper(self), state) diff --git a/rx/scheduler/historicalscheduler.py b/rx/scheduler/historicalscheduler.py index 3e54b9a6a..3dc5f5721 100644 --- a/rx/scheduler/historicalscheduler.py +++ b/rx/scheduler/historicalscheduler.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Optional from rx.core import typing @@ -10,7 +11,7 @@ class HistoricalScheduler(VirtualTimeScheduler): """Provides a virtual time scheduler that uses datetime for absolute time and timedelta for relative time.""" - def __init__(self, initial_clock: datetime = None) -> None: + def __init__(self, initial_clock: Optional[datetime] = None) -> None: """Creates a new historical scheduler with the specified initial clock value. @@ -30,7 +31,7 @@ def now(self) -> datetime: The scheduler's current time, as a datetime instance. """ - return self._clock + return self.to_datetime(self._clock) @classmethod def add( diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index dbdf3624f..92b5d2369 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -1,8 +1,8 @@ import logging import threading from abc import abstractmethod -from datetime import datetime -from typing import Optional, TypeVar +from datetime import datetime, timedelta +from typing import Optional, TypeVar, Union from rx.core import abc, typing from rx.internal import ArgumentOutOfRangeException, PriorityQueue @@ -21,7 +21,7 @@ class VirtualTimeScheduler(PeriodicScheduler): """Virtual Scheduler. This scheduler should work with either datetime/timespan or ticks as int/int""" - def __init__(self, initial_clock: int = 0) -> None: + def __init__(self, initial_clock: typing.AbsoluteTime = 0) -> None: """Creates a new virtual time scheduler with the specified initial clock value. @@ -139,7 +139,10 @@ def start(self) -> None: spinning = 0 elif spinning > MAX_SPINNING: - self._clock += 1.0 + if isinstance(self._clock, datetime): + self.clock += timedelta(microseconds=1000) + else: + self._clock += 1.0 spinning = 0 if not item.is_cancelled(): diff --git a/tests/test_core/test_notification.py b/tests/test_core/test_notification.py index e0890d409..069c559ba 100644 --- a/tests/test_core/test_notification.py +++ b/tests/test_core/test_notification.py @@ -1,3 +1,4 @@ +from typing import Any from rx.core.abc import ObserverBase from rx.testing import TestScheduler, ReactiveTest from rx.core.notification import OnNext, OnError, OnCompleted @@ -166,19 +167,19 @@ def test_throw_tostring(): assert "ex" in str(n1) -class CheckOnErrorObserver(ObserverBase): +class CheckOnErrorObserver(ObserverBase[Any]): def __init__(self): super(CheckOnErrorObserver, self).__init__() self.error = None - def on_next(value): + def on_next(self, value: Any) -> None: raise NotImplementedError() - def on_error(self, exception): - self.error = exception + def on_error(self, error: Exception) -> None: + self.error = error - def on_completed(self): + def on_completed(self) -> None: raise NotImplementedError() @@ -369,7 +370,10 @@ def create(): return OnNext(42).to_observable(scheduler) res = scheduler.start(create) - assert res.messages == [ReactiveTest.on_next(200, 42), ReactiveTest.on_completed(200)] + assert res.messages == [ + ReactiveTest.on_next(200, 42), + ReactiveTest.on_completed(200), + ] def test_to_observable_on_error(): diff --git a/tests/test_observable/test_fromfuture.py b/tests/test_observable/test_fromfuture.py index 068a527f0..3cc482233 100644 --- a/tests/test_observable/test_fromfuture.py +++ b/tests/test_observable/test_fromfuture.py @@ -6,7 +6,6 @@ class TestFromFuture(unittest.TestCase): - def test_future_success(self): loop = asyncio.get_event_loop() success = [False, True, False] @@ -29,14 +28,14 @@ def on_completed(): source.subscribe(on_next, on_error, on_completed) loop.run_until_complete(go()) - assert(all(success)) + assert all(success) def test_future_failure(self): loop = asyncio.get_event_loop() success = [True, False, True] async def go(): - error = Exception('woops') + error = Exception("woops") future = Future() future.set_exception(error) From e90917531c630b23807552962c80db1137d2bd68 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 22:08:59 +0100 Subject: [PATCH 077/103] Black formatting --- rx/core/observable/timer.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/rx/core/observable/timer.py b/rx/core/observable/timer.py index 3e8745155..cf807412a 100644 --- a/rx/core/observable/timer.py +++ b/rx/core/observable/timer.py @@ -10,8 +10,12 @@ 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): - _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): + _scheduler: abc.SchedulerBase = ( + scheduler or scheduler_ or TimeoutScheduler.singleton() + ) def action(scheduler: abc.SchedulerBase, state: Any): observer.on_next(0) @@ -27,7 +31,9 @@ def observable_timer_duetime_and_period( period: typing.AbsoluteOrRelativeTime, scheduler: Optional[abc.SchedulerBase] = None, ) -> Observable[int]: - def subscribe(observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None): + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() nonlocal duetime @@ -62,7 +68,9 @@ def action(scheduler: abc.SchedulerBase, state: Any): 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): + def subscribe( + observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None + ): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() d = _scheduler.to_seconds(duetime) @@ -88,7 +96,9 @@ def subscribe( observer: abc.ObserverBase[int], scheduler_: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - _scheduler: abc.SchedulerBase = scheduler or scheduler_ or TimeoutScheduler.singleton() + _scheduler: abc.SchedulerBase = ( + scheduler or scheduler_ or TimeoutScheduler.singleton() + ) def action(count: Optional[int] = None) -> Optional[int]: if count is not None: From d14e6fa37f3aee889053d47a4d61d9cfd577c9c1 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 22:11:41 +0100 Subject: [PATCH 078/103] Fix type issue. Value is not nullable (but can be None) --- rx/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rx/__init__.py b/rx/__init__.py index 1c0e4760a..57ac98597 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -1013,9 +1013,7 @@ def return_value( just = alias("just", "Alias for :func:`rx.return_value`.", return_value) -def repeat_value( - value: _T = None, repeat_count: Optional[int] = None -) -> Observable[_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. From 55105089a3732b881baf1917bf1302dbedad340a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 12 Feb 2022 22:20:45 +0100 Subject: [PATCH 079/103] Fixed type issues with subjects --- rx/subject/asyncsubject.py | 2 +- rx/subject/behaviorsubject.py | 4 ++-- rx/subject/innersubscription.py | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rx/subject/asyncsubject.py b/rx/subject/asyncsubject.py index 3989ccada..c1368bda0 100644 --- a/rx/subject/asyncsubject.py +++ b/rx/subject/asyncsubject.py @@ -82,5 +82,5 @@ def dispose(self) -> None: """Unsubscribe all observers and release resources.""" with self.lock: - self.value = None + self.value = cast(_T, None) super().dispose() diff --git a/rx/subject/behaviorsubject.py b/rx/subject/behaviorsubject.py index 17de74668..ccb3b6363 100644 --- a/rx/subject/behaviorsubject.py +++ b/rx/subject/behaviorsubject.py @@ -1,4 +1,4 @@ -from typing import Optional, TypeVar +from typing import Optional, TypeVar, cast from rx.core import abc from rx.disposable import Disposable @@ -66,5 +66,5 @@ def dispose(self) -> None: """ with self.lock: - self.value = None + self.value = cast(_T, None) super().dispose() diff --git a/rx/subject/innersubscription.py b/rx/subject/innersubscription.py index e0e47ebc3..290369ea8 100644 --- a/rx/subject/innersubscription.py +++ b/rx/subject/innersubscription.py @@ -1,5 +1,5 @@ import threading -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING, Optional, TypeVar from rx.core import abc @@ -10,7 +10,9 @@ class InnerSubscription(abc.DisposableBase): - def __init__(self, subject: "Subject[_T]", observer: abc.ObserverBase[_T]): + def __init__( + self, subject: "Subject[_T]", observer: Optional[abc.ObserverBase[_T]] = None + ): self.subject = subject self.observer = observer From d84ee1e2dc919b87a9e8270b1e5bace9fb061f31 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 08:41:38 +0100 Subject: [PATCH 080/103] Bump version to 4 - Because of Python 3.7 minimal version --- README.rst | 4 ++-- project.cfg | 2 +- rx/__init__.py | 2 +- setup.py | 8 +++++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 8dfdc6d14..34cc3cc1f 100644 --- a/README.rst +++ b/README.rst @@ -19,12 +19,12 @@ The Reactive Extensions for Python (RxPY) *A library for composing asynchronous and event-based programs using observable collections and query operator functions in Python* -RxPY v3.0 +RxPY v4.0 ---------------- For v1.X please go to the `v1 branch `_. -RxPY v3.x runs on `Python `_ 3.6 or above. To install +RxPY v4.x runs on `Python `_ 3.7 or above. To install RxPY: .. code:: console diff --git a/project.cfg b/project.cfg index 700e1a912..24bd11b7d 100644 --- a/project.cfg +++ b/project.cfg @@ -2,7 +2,7 @@ name = Rx project = RxPY # Please make sure the version here remains the same as in rx/__init__.py -version = 3.2.0 +version = 4.0.0 description = Reactive Extensions (Rx) for Python author = Dag Brattli author_email = dag@brattli.net diff --git a/rx/__init__.py b/rx/__init__.py index 57ac98597..fb0cd6d60 100644 --- a/rx/__init__.py +++ b/rx/__init__.py @@ -32,7 +32,7 @@ _G = TypeVar("_G") # Please make sure the version here remains the same as in project.cfg -__version__ = "3.2.0" +__version__ = "4.0.0" def amb(*sources: Observable[_T]) -> Observable[_T]: diff --git a/setup.py b/setup.py index 72556e344..3ab733e6b 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import sys +import sys try: from setuptools import setup @@ -39,7 +39,7 @@ 'download_url' )}, zip_safe=True, - python_requires='>=3.6.0', + python_requires='>=3.7.0', # https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ @@ -49,8 +49,10 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Software Development :: Libraries :: Python Modules' ], From db6ce147ae87d6ba0027974e2b8bb4f094af7edb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 08:41:51 +0100 Subject: [PATCH 081/103] Fix type issues with example --- examples/timeflies/timeflies_tkinter.py | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/timeflies/timeflies_tkinter.py b/examples/timeflies/timeflies_tkinter.py index f5b0c2cb7..f7d97a087 100644 --- a/examples/timeflies/timeflies_tkinter.py +++ b/examples/timeflies/timeflies_tkinter.py @@ -13,34 +13,34 @@ def main(): root.title("Rx for Python rocks") scheduler = TkinterScheduler(root) - mousemove: Subject[Event[Any]] = Subject() + mousemoves: Subject[Event[Any]] = Subject() frame = Frame(root, width=600, height=600) - - frame.bind("", mousemove.on_next) + frame.bind("", mousemoves.on_next) text = "TIME FLIES LIKE AN ARROW" - def on_next(info: Tuple[tkinter.Label, tkinter.Event[Any], int]): + def on_next(info: Tuple[tkinter.Label, "Event[Frame]", int]): label, ev, i = info label.place(x=ev.x + i * 12 + 15, y=ev.y) - def handle_label( - label: tkinter.Label, i: int - ) -> Observable[Tuple[tkinter.Label, tkinter.Event[Any], int]]: + def label2stream( + label: tkinter.Label, index: int + ) -> Observable[Tuple[tkinter.Label, "Event[Frame]", int]]: label.config(dict(borderwidth=0, padx=0, pady=0)) - mapper = ops.map(lambda ev: (label, ev, i)) - delayer = ops.delay(i * 0.1) - - return mousemove.pipe(delayer, mapper) + return mousemoves.pipe( + ops.map(lambda ev: (label, ev, index)), + ops.delay(index * 0.1), + ) - mapper = ops.map(lambda c: Label(frame, text=c)) - labeler = ops.flat_map_indexed(handle_label) + def char2label(char: str) -> Label: + return Label(frame, text=char) - rx.from_(text).pipe(mapper, labeler).subscribe( - on_next, on_error=print, scheduler=scheduler - ) + rx.from_(text).pipe( + ops.map(char2label), + ops.flat_map_indexed(label2stream), + ).subscribe(on_next, on_error=print, scheduler=scheduler) frame.pack() root.mainloop() From 9f9357bc46f813cd8a26a5f14bba5364aa4a4c10 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 08:42:16 +0100 Subject: [PATCH 082/103] Use typed function instead of lambda --- rx/core/operators/contains.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rx/core/operators/contains.py b/rx/core/operators/contains.py index 06d32c0ac..769f190af 100644 --- a/rx/core/operators/contains.py +++ b/rx/core/operators/contains.py @@ -12,7 +12,10 @@ def contains_( ) -> Callable[[Observable[_T]], Observable[bool]]: comparer_ = comparer or default_comparer - filtering = ops.filter(lambda v: comparer_(v, value)) + def predicate(v: _T) -> bool: + return comparer_(v, value) + + filtering = ops.filter(predicate) something = ops.some() return pipe(filtering, something) From 348427ef9cbea37a58b784ca7e9a5f351bf64122 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 08:42:35 +0100 Subject: [PATCH 083/103] Add type annotations --- tests/test_observable/test_filter.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_observable/test_filter.py b/tests/test_observable/test_filter.py index 470d5280d..4ae1abd01 100644 --- a/tests/test_observable/test_filter.py +++ b/tests/test_observable/test_filter.py @@ -1,10 +1,9 @@ import unittest -from rx import Observable -from rx.testing import TestScheduler, ReactiveTest, is_prime +from rx import Observable from rx.disposable import SerialDisposable - from rx.operators import filter, filter_indexed +from rx.testing import ReactiveTest, TestScheduler, is_prime on_next = ReactiveTest.on_next on_completed = ReactiveTest.on_completed @@ -51,7 +50,7 @@ def test_filter_complete(self): ) def create() -> Observable[int]: - def predicate(x): + def predicate(x: int) -> bool: invoked[0] += 1 return is_prime(x) @@ -160,7 +159,7 @@ def test_filter_dispose(self): ) def create(): - def predicate(x): + def predicate(x: int) -> bool: invoked[0] += 1 return is_prime(x) @@ -194,7 +193,7 @@ def test_filter_error(self): ) def create(): - def predicate(x): + def predicate(x: int) -> bool: invoked[0] += 1 return is_prime(x) @@ -235,7 +234,7 @@ def test_filter_on_error(self): ) def create(): - def predicate(x): + def predicate(x: int) -> bool: invoked[0] += 1 if x > 5: raise Exception(ex) From 35143e698de09561a8179d5123066905312b2469 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 10:04:35 +0100 Subject: [PATCH 084/103] Fixed default value handling --- rx/core/operators/defaultifempty.py | 10 +- rx/core/operators/last.py | 11 +- rx/core/operators/lastordefault.py | 17 ++- rx/core/operators/reduce.py | 5 +- rx/core/operators/scan.py | 6 +- rx/operators/__init__.py | 53 ++++++- tests/test_observable/test_defaultifempty.py | 33 ++-- tests/test_observable/test_lastordefault.py | 93 ++++++------ tests/test_observable/test_scan.py | 151 +++++++++++++------ 9 files changed, 263 insertions(+), 116 deletions(-) diff --git a/rx/core/operators/defaultifempty.py b/rx/core/operators/defaultifempty.py index 3af0682ec..10b9e5872 100644 --- a/rx/core/operators/defaultifempty.py +++ b/rx/core/operators/defaultifempty.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar from rx.core import Observable, abc @@ -6,9 +6,9 @@ def default_if_empty_( - default_value: _T = None, -) -> Callable[[Observable[_T]], Observable[_T]]: - def default_if_empty(source: Observable[_T]) -> Observable[_T]: + default_value: Optional[_T] = None, +) -> Callable[[Observable[_T]], Observable[Optional[_T]]]: + def default_if_empty(source: Observable[_T]) -> Observable[Optional[_T]]: """Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty. @@ -26,7 +26,7 @@ def default_if_empty(source: Observable[_T]) -> Observable[_T]: """ def subscribe( - observer: abc.ObserverBase[_T], + observer: abc.ObserverBase[Optional[_T]], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: found = [False] diff --git a/rx/core/operators/last.py b/rx/core/operators/last.py index 41ed8f1fd..569d8fe84 100644 --- a/rx/core/operators/last.py +++ b/rx/core/operators/last.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar +from typing import Any, Callable, Optional, TypeVar from rx import operators from rx.core import Observable @@ -11,8 +11,8 @@ def last_( predicate: Optional[Predicate[_T]] = None, -) -> Callable[[Observable[_T]], Observable[_T]]: - def last(source: Observable[_T]) -> Observable[_T]: +) -> Callable[[Observable[_T]], Observable[Any]]: + def last(source: Observable[_T]) -> Observable[Any]: """Partially applied last operator. Returns the last element of an observable sequence that @@ -32,7 +32,10 @@ def last(source: Observable[_T]) -> Observable[_T]: """ if predicate: - return source.pipe(operators.filter(predicate), operators.last()) + return source.pipe( + operators.filter(predicate), + operators.last(), + ) return last_or_default_async(source, False) diff --git a/rx/core/operators/lastordefault.py b/rx/core/operators/lastordefault.py index 750c79e01..d81ab08f7 100644 --- a/rx/core/operators/lastordefault.py +++ b/rx/core/operators/lastordefault.py @@ -8,10 +8,13 @@ def last_or_default_async( - source: Observable[_T], has_default: bool = False, default_value: Any = None -) -> Observable[_T]: + source: Observable[_T], + has_default: bool = False, + default_value: Optional[_T] = None, +) -> Observable[Optional[_T]]: def subscribe( - observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None + observer: abc.ObserverBase[Optional[_T]], + scheduler: Optional[abc.SchedulerBase] = None, ): value = [default_value] seen_value = [False] @@ -35,9 +38,9 @@ def on_completed(): def last_or_default( - predicate: Optional[typing.Predicate[_T]] = None, default_value: Any = None -) -> Callable[[Observable[_T]], Observable[_T]]: - def last_or_default(source: Observable[_T]) -> Observable[_T]: + default_value: Optional[_T] = None, predicate: Optional[typing.Predicate[_T]] = None +) -> Callable[[Observable[_T]], Observable[Any]]: + def last_or_default(source: Observable[Any]) -> Observable[Any]: """Return last or default element. Examples: @@ -54,7 +57,7 @@ def last_or_default(source: Observable[_T]) -> Observable[_T]: if predicate: return source.pipe( ops.filter(predicate), - ops.last_or_default(None, default_value), + ops.last_or_default(default_value), ) return last_or_default_async(source, True, default_value) diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 953d33674..0780659b9 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -37,7 +37,10 @@ def reduce_( if seed is not NotSet: seed_: _TState = cast(_TState, seed) scanner = ops.scan(accumulator, seed=seed_) - return pipe(scanner, ops.last_or_default(default_value=seed_)) + return pipe( + scanner, + ops.last_or_default(default_value=seed_), + ) return pipe(ops.scan(accumulator), ops.last()) diff --git a/rx/core/operators/scan.py b/rx/core/operators/scan.py index 271d2cdad..ef870c8a8 100644 --- a/rx/core/operators/scan.py +++ b/rx/core/operators/scan.py @@ -42,7 +42,11 @@ def projection(x: _T) -> _TState: if has_accumulation: accumulation = accumulator(accumulation, x) else: - accumulation = accumulator(seed, x) if has_seed else x + accumulation = ( + accumulator(cast(_TState, seed), x) + if has_seed + else cast(_TState, x) + ) has_accumulation = True return accumulation diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 7c56c77d3..d49f20b6f 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -551,9 +551,21 @@ def debounce( throttle_with_timeout = debounce +@overload def default_if_empty( - default_value: _T = None, + default_value: _T, ) -> Callable[[Observable[_T]], Observable[_T]]: + ... + + +@overload +def default_if_empty() -> Callable[[Observable[_T]], Observable[Optional[_T]]]: + ... + + +def default_if_empty( + default_value: Any = None, +) -> Callable[[Observable[Any]], Observable[Any]]: """Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty. @@ -1673,9 +1685,30 @@ def last( return last_(predicate) +@overload +def last_or_default() -> Callable[[Observable[_T]], Observable[Optional[_T]]]: + ... + + +@overload def last_or_default( - predicate: Optional[Predicate[_T]] = None, default_value: Optional[_T] = None + default_value: _T, +) -> Callable[[Observable[_T]], Observable[_T]]: + ... + + +@overload +def last_or_default( + default_value: _T, + predicate: Predicate[_T], ) -> Callable[[Observable[_T]], Observable[_T]]: + ... + + +def last_or_default( + default_value: Any = None, + predicate: Optional[Predicate[_T]] = None, +) -> Callable[[Observable[_T]], Observable[Any]]: """The last_or_default operator. Returns the last element of an observable sequence that satisfies @@ -1710,7 +1743,7 @@ def last_or_default( """ from rx.core.operators.lastordefault import last_or_default - return last_or_default(predicate, default_value) + return last_or_default(default_value, predicate) def map( @@ -2451,6 +2484,20 @@ def sample( return sample_(sampler, scheduler) +@overload +def scan( + accumulator: Accumulator[_T, _T] +) -> Callable[[Observable[_T]], Observable[_T]]: + ... + + +@overload +def scan( + accumulator: Accumulator[_TState, _T], seed: _TState +) -> Callable[[Observable[_T]], Observable[_TState]]: + ... + + def scan( accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet ) -> Callable[[Observable[_T]], Observable[_TState]]: diff --git a/tests/test_observable/test_defaultifempty.py b/tests/test_observable/test_defaultifempty.py index 1e8cc4aa8..84b38a703 100644 --- a/tests/test_observable/test_defaultifempty.py +++ b/tests/test_observable/test_defaultifempty.py @@ -1,6 +1,7 @@ +from typing import Optional import unittest -from rx import operators as ops +from rx import Observable, operators as ops from rx.testing import TestScheduler, ReactiveTest on_next = ReactiveTest.on_next @@ -16,34 +17,43 @@ class RxException(Exception): pass -# Helper function for raising exceptions within lambdas -def _raise(ex): - raise RxException(ex) - - class TestDistinctUntilChanged(unittest.TestCase): def test_default_if_empty_non_empty1(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(280, 42), on_next(360, 43), on_completed(420)) + xs = scheduler.create_hot_observable( + on_next(280, 42), + on_next(360, 43), + on_completed(420), + ) - def create(): + def create() -> Observable[Optional[int]]: return xs.pipe(ops.default_if_empty()) results = scheduler.start(create) - assert results.messages == [on_next(280, 42), on_next(360, 43), on_completed(420)] + assert results.messages == [ + on_next(280, 42), + on_next(360, 43), + on_completed(420), + ] assert xs.subscriptions == [subscribe(200, 420)] def test_default_if_empty_non_empty2(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(280, 42), on_next(360, 43), on_completed(420)) + xs = scheduler.create_hot_observable( + on_next(280, 42), on_next(360, 43), on_completed(420) + ) def create(): return xs.pipe(ops.default_if_empty(-1)) results = scheduler.start(create) - assert results.messages == [on_next(280, 42), on_next(360, 43), on_completed(420)] + assert results.messages == [ + on_next(280, 42), + on_next(360, 43), + on_completed(420), + ] assert xs.subscriptions == [subscribe(200, 420)] def test_default_if_empty_empty1(self): @@ -64,6 +74,7 @@ def test_default_if_empty_empty2(self): def create(): return xs.pipe(ops.default_if_empty(-1)) + results = scheduler.start(create) assert results.messages == [on_next(420, -1), on_completed(420)] diff --git a/tests/test_observable/test_lastordefault.py b/tests/test_observable/test_lastordefault.py index d02bd999c..260cb13ca 100644 --- a/tests/test_observable/test_lastordefault.py +++ b/tests/test_observable/test_lastordefault.py @@ -1,7 +1,8 @@ import unittest from rx import operators as _ -from rx.testing import TestScheduler, ReactiveTest +from rx.core.observable.observable import Observable +from rx.testing import ReactiveTest, TestScheduler on_next = ReactiveTest.on_next on_completed = ReactiveTest.on_completed @@ -12,23 +13,13 @@ created = ReactiveTest.created -class RxException(Exception): - pass - - -# Helper function for raising exceptions within lambdas -def _raise(ex): - raise RxException(ex) - - class TestLastOrDefault(unittest.TestCase): def test_last_or_default_async_empty(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable( - on_next(150, 1), on_completed(250)) + xs = scheduler.create_hot_observable(on_next(150, 1), on_completed(250)) - def create(): - return xs.pipe(_.last_or_default(None, 0)) + def create() -> Observable[int]: + return xs.pipe(_.last_or_default(default_value=0)) res = scheduler.start(create=create) @@ -38,10 +29,11 @@ def create(): def test_last_or_default_async(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_completed(250)) + on_next(150, 1), on_next(210, 2), on_completed(250) + ) - def create(): - return xs.pipe(_.last_or_default(None, 0)) + def create() -> Observable[int]: + return xs.pipe(_.last_or_default(0)) res = scheduler.start(create=create) @@ -51,11 +43,14 @@ def create(): def test_last_or_default_async_many(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_next(220, 3), - on_completed(250)) + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_completed(250), + ) def create(): - return xs.pipe(_.last_or_default(None, 0)) + return xs.pipe(_.last_or_default(0)) res = scheduler.start(create=create) @@ -63,30 +58,34 @@ def create(): assert xs.subscriptions == [subscribe(200, 250)] def test_last_or_default_async_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(210, ex)) def create(): - return xs.pipe(_.last_or_default(None, 0)) + return xs.pipe(_.last_or_default(0)) res = scheduler.start(create=create) assert res.messages == [on_error(210, ex)] assert xs.subscriptions == [subscribe(200, 210)] - def test_last_or_default_async_predicate(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_next(220, 3), - on_next(230, 4), on_next(240, 5), on_completed(250)) - - def create(): - def predicate(x): + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) + + def create() -> Observable[int]: + def predicate(x: int) -> bool: return x % 2 == 1 - return xs.pipe(_.last_or_default(predicate, 0)) + return xs.pipe(_.last_or_default(0, predicate)) res = scheduler.start(create=create) @@ -96,14 +95,19 @@ def predicate(x): def test_last_or_default_async_Predicate_none(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_next(220, 3), - on_next(230, 4), on_next(240, 5), on_completed(250)) + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) def create(): - def predicate(x): + def predicate(x: int) -> bool: return x > 10 - return xs.pipe(_.last_or_default(predicate, 0)) + return xs.pipe(_.last_or_default(0, predicate)) res = scheduler.start(create=create) @@ -111,14 +115,15 @@ def predicate(x): assert xs.subscriptions == [subscribe(200, 250)] def test_last_or_default_async_Predicate_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(210, ex)) def create(): - def predicate(x): + def predicate(x: int) -> bool: return x > 10 - return xs.pipe(_.last_or_default(predicate, 0)) + + return xs.pipe(_.last_or_default(0, predicate)) res = scheduler.start(create=create) @@ -126,23 +131,27 @@ def predicate(x): assert xs.subscriptions == [subscribe(200, 210)] def test_last_or_default_async_predicate_throws(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_next(220, 3), - on_next(230, 4), on_next(240, 5), on_completed(250)) + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) def create(): - def predicate(x): + def predicate(x: int) -> bool: if x < 4: return x % 2 == 1 else: raise Exception(ex) - return xs.pipe(_.last_or_default(predicate, 0)) + return xs.pipe(_.last_or_default(0, predicate)) res = scheduler.start(create=create) assert res.messages == [on_error(230, ex)] assert xs.subscriptions == [subscribe(200, 230)] - diff --git a/tests/test_observable/test_scan.py b/tests/test_observable/test_scan.py index dd1b63937..e8413b4ec 100644 --- a/tests/test_observable/test_scan.py +++ b/tests/test_observable/test_scan.py @@ -1,6 +1,6 @@ import unittest -from rx import never, operators as _ +from rx import Observable, never, operators as _ from rx.testing import TestScheduler, ReactiveTest on_next = ReactiveTest.on_next @@ -13,14 +13,14 @@ class TestScan(unittest.TestCase): - def test_scan_seed_never(self): scheduler = TestScheduler() seed = 42 def create(): - def func(acc, x): + def func(acc: int, x: int) -> int: return acc + x + return never().pipe(_.scan(seed=seed, accumulator=func)) results = scheduler.start(create) @@ -35,57 +35,88 @@ def create(): return xs.pipe(_.scan(lambda acc, x: acc + x, seed=seed)) results = scheduler.start(create).messages - assert(len(results) == 1) - assert(results[0].value.kind == 'C' and results[0].time == 250) + assert len(results) == 1 + assert results[0].value.kind == "C" and results[0].time == 250 def test_scan_seed_return(self): scheduler = TestScheduler() seed = 42 - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(220, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(220, 2), on_completed(250) + ) def create(): return xs.pipe(_.scan(lambda acc, x: acc + x, seed=seed)) results = scheduler.start(create).messages - assert(len(results) == 2) - assert(results[0].value.kind == 'N' and results[0].value.value == seed + 2 and results[0].time == 220) - assert(results[1].value.kind == 'C' and results[1].time == 250) + assert len(results) == 2 + assert ( + results[0].value.kind == "N" + and results[0].value.value == seed + 2 + and results[0].time == 220 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_scan_seed_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() seed = 42 xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) - def create(): - return xs.pipe(_.scan(seed, lambda acc, x: acc + x)) + def create() -> Observable[int]: + return xs.pipe(_.scan(lambda acc, x: acc + x, seed)) results = scheduler.start(create).messages - assert(len(results) == 1) - assert(results[0].value.kind == 'E' and results[0].value.exception == ex and results[0].time == 250) + assert len(results) == 1 + assert ( + results[0].value.kind == "E" + and results[0].value.exception == ex + and results[0].time == 250 + ) def test_scan_seed_somedata(self): scheduler = TestScheduler() seed = 1 - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 3), on_next(230, 4), on_next(240, 5), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) def create(): return xs.pipe(_.scan(lambda acc, x: acc + x, seed=seed)) results = scheduler.start(create).messages - assert(len(results) == 5) - assert(results[0].value.kind == 'N' and results[0].value.value == seed + 2 and results[0].time == 210) - assert(results[1].value.kind == 'N' and results[1].value.value == seed + 2 + 3 and results[1].time == 220) - assert(results[2].value.kind == 'N' and results[2].value.value == seed + 2 + 3 + 4 and results[2].time == 230) - assert(results[3].value.kind == 'N' and results[3].value.value == - seed + 2 + 3 + 4 + 5 and results[3].time == 240) - assert(results[4].value.kind == 'C' and results[4].time == 250) + assert len(results) == 5 + assert ( + results[0].value.kind == "N" + and results[0].value.value == seed + 2 + and results[0].time == 210 + ) + assert ( + results[1].value.kind == "N" + and results[1].value.value == seed + 2 + 3 + and results[1].time == 220 + ) + assert ( + results[2].value.kind == "N" + and results[2].value.value == seed + 2 + 3 + 4 + and results[2].time == 230 + ) + assert ( + results[3].value.kind == "N" + and results[3].value.value == seed + 2 + 3 + 4 + 5 + and results[3].time == 240 + ) + assert results[4].value.kind == "C" and results[4].time == 250 def test_scan_noseed_never(self): scheduler = TestScheduler() - def create(): + def create() -> Observable[int]: return never().pipe(_.scan(lambda acc, x: acc + x)) results = scheduler.start(create) @@ -95,61 +126,97 @@ def test_scan_noseed_empty(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_completed(250)) - def create(): + def create() -> Observable[int]: return xs.pipe(_.scan(lambda acc, x: acc + x)) results = scheduler.start(create).messages assert len(results) == 1 - assert results[0].value.kind == 'C' and results[0].time == 250 + assert results[0].value.kind == "C" and results[0].time == 250 def test_scan_noseed_return(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(220, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(220, 2), on_completed(250) + ) - def create(): - def func(acc, x): + def create() -> Observable[int]: + def func(acc: int, x: int) -> int: if acc is None: acc = 0 return acc + x + return xs.pipe(_.scan(accumulator=func)) results = scheduler.start(create).messages assert len(results) == 2 - assert results[0].value.kind == 'N' and results[0].time == 220 and results[0].value.value == 2 - assert results[1].value.kind == 'C' and results[1].time == 250 + assert ( + results[0].value.kind == "N" + and results[0].time == 220 + and results[0].value.value == 2 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_scan_noseed_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) - def create(): - def func(acc, x): + def create() -> Observable[int]: + def func(acc: int, x: int) -> int: if acc is None: acc = 0 return acc + x + return xs.pipe(_.scan(func)) + results = scheduler.start(create).messages assert len(results) == 1 - assert results[0].value.kind == 'E' and results[0].time == 250 and results[0].value.exception == ex + assert ( + results[0].value.kind == "E" + and results[0].time == 250 + and results[0].value.exception == ex + ) def test_scan_noseed_somedata(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 3), on_next(230, 4), on_next(240, 5), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) def create(): def func(acc, x): if acc is None: acc = 0 return acc + x + return xs.pipe(_.scan(func)) results = scheduler.start(create).messages assert len(results) == 5 - assert results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2 - assert results[1].value.kind == 'N' and results[1].time == 220 and results[1].value.value == 2 + 3 - assert results[2].value.kind == 'N' and results[2].time == 230 and results[2].value.value == 2 + 3 + 4 - assert results[3].value.kind == 'N' and results[3].time == 240 and results[3].value.value == 2 + 3 + 4 + 5 - assert results[4].value.kind == 'C' and results[4].time == 250 + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert ( + results[1].value.kind == "N" + and results[1].time == 220 + and results[1].value.value == 2 + 3 + ) + assert ( + results[2].value.kind == "N" + and results[2].time == 230 + and results[2].value.value == 2 + 3 + 4 + ) + assert ( + results[3].value.kind == "N" + and results[3].time == 240 + and results[3].value.value == 2 + 3 + 4 + 5 + ) + assert results[4].value.kind == "C" and results[4].time == 250 From b5ad3526f09b273659635cd8aea34b3e77c574d9 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 12:43:21 +0100 Subject: [PATCH 085/103] Fixed typing for groupjoin --- rx/core/observable/fromcallback.py | 2 +- rx/core/operators/do.py | 11 +++++++- rx/core/operators/elementatordefault.py | 7 ++--- rx/core/operators/exclusive.py | 2 +- rx/core/operators/groupjoin.py | 34 +++++++++++++------------ rx/operators/__init__.py | 10 +++++--- 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/rx/core/observable/fromcallback.py b/rx/core/observable/fromcallback.py index 1a02c09a6..de6514eea 100644 --- a/rx/core/observable/fromcallback.py +++ b/rx/core/observable/fromcallback.py @@ -40,7 +40,7 @@ def handler(*args: Any): observer.on_next(results) else: - if isinstance(results, list) and len(results) <= 1: + if len(results) <= 1: observer.on_next(*results) else: observer.on_next(results) diff --git a/rx/core/operators/do.py b/rx/core/operators/do.py index 59e31a720..2fb2b8666 100644 --- a/rx/core/operators/do.py +++ b/rx/core/operators/do.py @@ -315,4 +315,13 @@ def on_error(exception: Exception): return partial -__all__ = ["do_", "do_action_"] +__all__ = [ + "do_", + "do_action_", + "do_after_next", + "do_finally", + "do_on_dispose", + "do_on_subscribe", + "do_on_terminate", + "do_after_terminate", +] diff --git a/rx/core/operators/elementatordefault.py b/rx/core/operators/elementatordefault.py index 85a33cd36..405943ef0 100644 --- a/rx/core/operators/elementatordefault.py +++ b/rx/core/operators/elementatordefault.py @@ -17,13 +17,14 @@ def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - i = [index] + index_ = index def on_next(x: _T) -> None: + nonlocal index_ found = False with source.lock: - if i[0]: - i[0] -= 1 + if index_: + index_ -= 1 else: found = True diff --git a/rx/core/operators/exclusive.py b/rx/core/operators/exclusive.py index 3413a70e2..1d8d7bd8c 100644 --- a/rx/core/operators/exclusive.py +++ b/rx/core/operators/exclusive.py @@ -53,7 +53,7 @@ def on_completed_inner(): observer.on_next, observer.on_error, on_completed_inner, - scheduler, + scheduler=scheduler, ) def on_completed() -> None: diff --git a/rx/core/operators/groupjoin.py b/rx/core/operators/groupjoin.py index 9a0702d3c..6f90cc676 100644 --- a/rx/core/operators/groupjoin.py +++ b/rx/core/operators/groupjoin.py @@ -12,17 +12,17 @@ from rx.internal.utils import add_ref from rx.subject import Subject -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") +_TLeft = TypeVar("_TLeft") +_TRight = TypeVar("_TRight") log = logging.getLogger("Rx") def group_join_( - right: Observable[_T2], - left_duration_mapper: Callable[[_T1], Observable[Any]], - right_duration_mapper: Callable[[_T2], Observable[Any]], -) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: + right: Observable[_TRight], + left_duration_mapper: Callable[[_TLeft], Observable[Any]], + right_duration_mapper: Callable[[_TRight], Observable[Any]], +) -> Callable[[Observable[_TLeft]], Observable[Tuple[_TLeft, Observable[_TRight]]]]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. @@ -40,23 +40,25 @@ def group_join_( from source elements that have an overlapping duration. """ - def nothing(_): + def nothing(_: Any) -> None: return None - def group_join(left: Observable[_T1]) -> Observable[Tuple[_T1, _T2]]: + def group_join( + left: Observable[_TLeft], + ) -> Observable[Tuple[_TLeft, Observable[_TRight]]]: def subscribe( - observer: abc.ObserverBase[Tuple[_T1, _T2]], + observer: abc.ObserverBase[Tuple[_TLeft, Observable[_TRight]]], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: group = CompositeDisposable() rcd = RefCountDisposable(group) - left_map = OrderedDict() - right_map = OrderedDict() + left_map: OrderedDict[int, Subject[_TRight]] = OrderedDict() + right_map: OrderedDict[int, _TRight] = OrderedDict() left_id = [0] right_id = [0] - def on_next_left(value): - subject = Subject() + def on_next_left(value: _TLeft) -> None: + subject: Subject[_TRight] = Subject() with left.lock: _id = left_id[0] @@ -122,7 +124,7 @@ def on_error_left(error: Exception) -> None: ) ) - def send_right(value): + def send_right(value: _TRight) -> None: with left.lock: _id = right_id[0] right_id[0] += 1 @@ -144,7 +146,7 @@ def expire(): observer.on_error(e) return - def on_error(error): + def on_error(error: Exception): with left.lock: for left_value in left_map.values(): left_value.on_error(error) @@ -159,7 +161,7 @@ def on_error(error): for left_value in left_map.values(): left_value.on_next(value) - def on_error_right(error): + def on_error_right(error: Exception) -> None: for left_value in left_map.values(): left_value.on_error(error) diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index d49f20b6f..f7db3493e 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -46,6 +46,8 @@ _TKey = TypeVar("_TKey") _TState = TypeVar("_TState") _TValue = TypeVar("_TValue") +_TRight = TypeVar("_TRight") +_TLeft = TypeVar("_TLeft") _A = TypeVar("_A") _B = TypeVar("_B") @@ -1537,10 +1539,10 @@ def group_by_until( def group_join( - right: Observable[_T2], - left_duration_mapper: Callable[[_T1], Observable[Any]], - right_duration_mapper: Callable[[_T2], Observable[Any]], -) -> Callable[[Observable[_T1]], Observable[Tuple[_T1, _T2]]]: + right: Observable[_TRight], + left_duration_mapper: Callable[[_TLeft], Observable[Any]], + right_duration_mapper: Callable[[_TRight], Observable[Any]], +) -> Callable[[Observable[_TLeft]], Observable[Tuple[_TLeft, Observable[_TRight]]]]: """Correlates the elements of two sequences based on overlapping durations, and groups the results. From 4ea1012eac2d93be51337a159d71b889f3eea2e7 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 12:43:41 +0100 Subject: [PATCH 086/103] Move statistics operators to examples --- {rx/core/operators => examples/statistics}/statistics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename {rx/core/operators => examples/statistics}/statistics.py (96%) diff --git a/rx/core/operators/statistics.py b/examples/statistics/statistics.py similarity index 96% rename from rx/core/operators/statistics.py rename to examples/statistics/statistics.py index 8b733c1f2..3b07453c4 100644 --- a/rx/core/operators/statistics.py +++ b/examples/statistics/statistics.py @@ -1,4 +1,5 @@ import math +from typing import Any from rx.core import Observable @@ -22,7 +23,7 @@ def median(source: Observable) -> Observable: return source.to_sorted_list().map(lambda l: determine_median(l)) -def mode(source: Observable) -> Observable: +def mode(source: Observable[Any]) -> Observable[Any]: """ Returns the most frequently emitted value (or "values" if they have the same number of occurrences). The sequence must be finite. From 85f875db7b77354b94d439b90c2838debbb20928 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 13:19:13 +0100 Subject: [PATCH 087/103] Typing fixes --- rx/core/operators/publishvalue.py | 10 +++++++--- rx/core/operators/timeinterval.py | 20 ++++++++++++-------- rx/core/operators/timeoutwithmapper.py | 6 +++++- rx/core/operators/window.py | 4 ++-- rx/operators/__init__.py | 6 +++++- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index 7f2caf95e..e201ba745 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, cast from rx import operators as ops from rx.core import Observable, abc @@ -14,11 +14,15 @@ def publish_value_( ) -> Callable[[Observable[_T1]], Observable[_T2]]: if mapper: - def subject_factory(scheduler: abc.SchedulerBase) -> BehaviorSubject[_T1]: + def subject_factory( + scheduler: Optional[abc.SchedulerBase] = None, + ) -> BehaviorSubject[_T1]: return BehaviorSubject(initial_value) return ops.multicast(subject_factory=subject_factory, mapper=mapper) - return ops.multicast(BehaviorSubject(initial_value)) + + subject = BehaviorSubject(cast(_T2, initial_value)) + return ops.multicast(subject) __all__ = ["publish_value_"] diff --git a/rx/core/operators/timeinterval.py b/rx/core/operators/timeinterval.py index aec6122a9..927fa6ff5 100644 --- a/rx/core/operators/timeinterval.py +++ b/rx/core/operators/timeinterval.py @@ -1,5 +1,6 @@ +from dataclasses import dataclass from datetime import timedelta -from typing import Any, Callable, NamedTuple, Optional, TypeVar +from typing import Callable, Generic, Optional, TypeVar from rx import operators as ops from rx.core import Observable, abc @@ -8,15 +9,16 @@ _T = TypeVar("_T") -class TimeInterval(NamedTuple): - value: Any +@dataclass +class TimeInterval(Generic[_T]): + value: _T interval: timedelta def time_interval_( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[_T]]: - def time_interval(source: Observable[_T]) -> Observable[_T]: +) -> Callable[[Observable[_T]], Observable[TimeInterval[_T]]]: + def time_interval(source: Observable[_T]) -> Observable[TimeInterval[_T]]: """Records the time interval between consecutive values in an observable sequence. @@ -28,13 +30,13 @@ def time_interval(source: Observable[_T]) -> Observable[_T]: """ def subscribe( - observer: abc.ObserverBase[_T], + observer: abc.ObserverBase[TimeInterval[_T]], scheduler_: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() last = _scheduler.now - def mapper(value: _T) -> TimeInterval: + def mapper(value: _T) -> TimeInterval[_T]: nonlocal last now = _scheduler.now @@ -42,7 +44,9 @@ def mapper(value: _T) -> TimeInterval: last = now return TimeInterval(value=value, interval=span) - return source.pipe(ops.map(mapper)).subscribe(observer, _scheduler) + return source.pipe(ops.map(mapper)).subscribe( + observer, scheduler=_scheduler + ) return Observable(subscribe) diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index 7d3139ba7..170a0b2f3 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -98,7 +98,11 @@ def on_next(x: _T) -> None: observer.on_next(x) timeout = None try: - timeout = timeout_duration_mapper(x) + timeout = ( + timeout_duration_mapper(x) + if timeout_duration_mapper + else rx.never() + ) except Exception as e: observer.on_error(e) return diff --git a/rx/core/operators/window.py b/rx/core/operators/window.py index 46915d99f..cfc60ecda 100644 --- a/rx/core/operators/window.py +++ b/rx/core/operators/window.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Callable, Optional, TypeVar +from typing import Any, Callable, Optional, Tuple, TypeVar from rx import empty from rx import operators as ops @@ -33,7 +33,7 @@ def window_toggle_( """ def window_toggle(source: Observable[_T]) -> Observable[Observable[_T]]: - def mapper(args): + def mapper(args: Tuple[Any, Observable[_T]]): _, window = args return window diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index f7db3493e..d75649855 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -3688,9 +3688,13 @@ def timeout_with_mapper( return timeout_with_mapper_(first_timeout, timeout_duration_mapper, other) +if TYPE_CHECKING: + from rx.core.operators.timeinterval import TimeInterval + + def time_interval( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[_T]]: +) -> Callable[[Observable[_T]], Observable[TimeInterval[_T]]]: """Records the time interval between consecutive values in an observable sequence. From 080e6cfe4c7aabf49890c4f61018520d18db4df0 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 13:34:08 +0100 Subject: [PATCH 088/103] Use string types for older Python version --- rx/operators/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index d75649855..ec5004a25 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -3694,7 +3694,7 @@ def timeout_with_mapper( def time_interval( scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T]], Observable[TimeInterval[_T]]]: +) -> Callable[[Observable[_T]], Observable["TimeInterval[_T]"]]: """Records the time interval between consecutive values in an observable sequence. From 6cc555c5c7d22562195e6473b622d66165a68d0c Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 14:11:30 +0100 Subject: [PATCH 089/103] Type fixes (mypy) for average --- rx/core/operators/average.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rx/core/operators/average.py b/rx/core/operators/average.py index 5afce2315..560984584 100644 --- a/rx/core/operators/average.py +++ b/rx/core/operators/average.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from typing import Any, Callable, Optional, TypeVar, cast from rx import operators @@ -6,16 +7,16 @@ _T = TypeVar("_T") -class AverageValue(object): - def __init__(self, sum: float, count: int): - self.sum = sum - self.count = count +@dataclass +class AverageValue: + sum: float + count: int def average_( key_mapper: Optional[typing.Mapper[_T, float]] = None, ) -> Callable[[Observable[_T]], Observable[float]]: - def average(source: Observable[_T]) -> Observable[float]: + def average(source: Observable[Any]) -> Observable[float]: """Partially applied average operator. Computes the average of an observable sequence of values that From f5ea25c6bd971d05ef33608dd35dada274662123 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 13 Feb 2022 14:26:55 +0100 Subject: [PATCH 090/103] Fixed sum, timeout and takeuntilwith... --- rx/core/operators/sum.py | 13 +++++++------ rx/core/operators/takeuntilwithtime.py | 11 +++++------ rx/core/operators/timeout.py | 10 ++++------ rx/operators/__init__.py | 14 ++++++++++++-- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/rx/core/operators/sum.py b/rx/core/operators/sum.py index b0d29dc53..91f459cef 100644 --- a/rx/core/operators/sum.py +++ b/rx/core/operators/sum.py @@ -1,19 +1,20 @@ -from typing import Callable, Optional, TypeVar +from typing import Any, Callable, Optional from rx import operators as ops from rx.core import Observable, pipe from rx.core.typing import Mapper -_T = TypeVar("_T") - def sum_( - key_mapper: Optional[Mapper[_T, int]] = None -) -> Callable[[Observable[_T]], Observable[int]]: + key_mapper: Optional[Mapper[Any, float]] = None +) -> Callable[[Observable[Any]], Observable[float]]: if key_mapper: return pipe(ops.map(key_mapper), ops.sum()) - return ops.reduce(seed=0, accumulator=lambda prev, curr: prev + curr) + def accumulator(prev: float, cur: float) -> float: + return prev + cur + + return ops.reduce(seed=0, accumulator=accumulator) __all__ = ["sum_"] diff --git a/rx/core/operators/takeuntilwithtime.py b/rx/core/operators/takeuntilwithtime.py index 10aa77d4d..7f66e5afc 100644 --- a/rx/core/operators/takeuntilwithtime.py +++ b/rx/core/operators/takeuntilwithtime.py @@ -33,15 +33,14 @@ def subscribe( ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - if isinstance(end_time, datetime): - scheduler_method = _scheduler.schedule_absolute - else: - scheduler_method = _scheduler.schedule_relative - def action(scheduler: abc.SchedulerBase, state: Any = None): observer.on_completed() - task = scheduler_method(end_time, action) + if isinstance(end_time, datetime): + task = _scheduler.schedule_absolute(end_time, action) + else: + task = _scheduler.schedule_relative(end_time, action) + return CompositeDisposable( task, source.subscribe(observer, scheduler=scheduler_) ) diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index a4a3dc327..7a81af930 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -47,11 +47,6 @@ def subscribe( ) -> abc.DisposableBase: _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - if isinstance(duetime, datetime): - scheduler_method = _scheduler.schedule_absolute - else: - scheduler_method = _scheduler.schedule_relative - switched = [False] _id = [0] @@ -71,7 +66,10 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): observer, scheduler=scheduler ) - timer.disposable = scheduler_method(duetime, action) + if isinstance(duetime, datetime): + timer.disposable = _scheduler.schedule_absolute(duetime, action) + else: + timer.disposable = _scheduler.schedule_relative(duetime, action) create_timer() diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index ec5004a25..517aa1374 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -3181,9 +3181,19 @@ def subscribe_on( return subscribe_on_(scheduler) +@overload +def sum() -> Callable[[Observable[float]], Observable[float]]: + ... + + +@overload +def sum(key_mapper: Mapper[_T, float]) -> Callable[[Observable[_T]], Observable[float]]: + ... + + def sum( - key_mapper: Optional[Mapper[_T, int]] = None -) -> Callable[[Observable[_T]], Observable[int]]: + key_mapper: Optional[Mapper[Any, float]] = None +) -> Callable[[Observable[Any]], Observable[float]]: """Computes the sum of a sequence of values that are obtained by invoking an optional transform function on each element of the input sequence, else if not specified computes the sum on each item From c6e11efb0bf7a785f9e141bf1c44ae60129a728b Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 17:12:44 +0100 Subject: [PATCH 091/103] Refactor multicast (wip) --- rx/core/observable/observable.py | 24 -- rx/core/operators/multicast.py | 26 +- rx/core/operators/timeout.py | 4 +- rx/core/operators/tofuture.py | 5 +- rx/operators/__init__.py | 28 ++- tests/test_observable/test_multicast.py | 319 +++++++++++++++++++----- 6 files changed, 298 insertions(+), 108 deletions(-) diff --git a/rx/core/observable/observable.py b/rx/core/observable/observable.py index 9fdeae3d2..25b1b2259 100644 --- a/rx/core/observable/observable.py +++ b/rx/core/observable/observable.py @@ -142,30 +142,6 @@ def set_disposable(_: Optional[abc.SchedulerBase] = None, __: Any = None): # Hide the identity of the auto detach observer return Disposable(auto_detach_observer.dispose) - @overload - def pipe( - self, *operators: Callable[[Observable[_T]], Observable[_A]] - ) -> Observable[_A]: # pylint: disable=no-self-use - """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. - """ - ... - @overload def pipe(self, __op1: Callable[[Observable[_T]], _A]) -> _A: ... diff --git a/rx/core/operators/multicast.py b/rx/core/operators/multicast.py index a66e0cf0a..0191bd238 100644 --- a/rx/core/operators/multicast.py +++ b/rx/core/operators/multicast.py @@ -1,18 +1,23 @@ from typing import Callable, Optional, TypeVar, Union +from rx import operators as ops from rx.core import ConnectableObservable, Observable, abc from rx.disposable import CompositeDisposable -_T = TypeVar("_T") +_TSource = TypeVar("_TSource") +_TResult = TypeVar("_TResult") def multicast_( - subject: Optional[abc.SubjectBase[_T]] = None, + subject: Optional[abc.SubjectBase[_TSource]] = None, + *, subject_factory: Optional[ - Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_T]] + Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_TSource]] ] = None, - mapper: Optional[Callable[[ConnectableObservable[_T]], Observable[_T]]] = None, -) -> Callable[[Observable[_T]], Union[Observable[_T], ConnectableObservable[_T]]]: + mapper: Optional[Callable[[Observable[_TSource]], Observable[_TResult]]] = None, +) -> Callable[ + [Observable[_TSource]], Union[Observable[_TResult], ConnectableObservable[_TSource]] +]: """Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a mapper function. Each subscription to the resulting sequence causes a @@ -41,16 +46,17 @@ def multicast_( """ def multicast( - source: Observable[_T], - ) -> Union[Observable[_T], ConnectableObservable[_T]]: + source: Observable[_TSource], + ) -> Union[Observable[_TSource], ConnectableObservable[_TSource]]: if subject_factory: def subscribe( - observer: abc.ObserverBase[_T], + observer: abc.ObserverBase[_TSource], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: + assert subject_factory connectable = source.pipe( - multicast_(subject=subject_factory(scheduler)) + ops.multicast(subject=subject_factory(scheduler)) ) assert mapper subscription = mapper(connectable).subscribe( @@ -60,7 +66,7 @@ def subscribe( return CompositeDisposable(subscription, connectable.connect(scheduler)) return Observable(subscribe) - ret: ConnectableObservable[_T] = ConnectableObservable(source, subject) + ret: ConnectableObservable[_TSource] = ConnectableObservable(source, subject) return ret return multicast diff --git a/rx/core/operators/timeout.py b/rx/core/operators/timeout.py index 7a81af930..444fb9e76 100644 --- a/rx/core/operators/timeout.py +++ b/rx/core/operators/timeout.py @@ -55,7 +55,7 @@ def subscribe( timer = SerialDisposable() subscription.disposable = original - def create_timer(): + def create_timer() -> None: my_id = _id[0] def action(scheduler: abc.SchedulerBase, state: Any = None): @@ -86,7 +86,7 @@ def on_error(error: Exception) -> None: _id[0] += 1 observer.on_error(error) - def on_completed(): + def on_completed() -> None: on_completed_wins = not switched[0] if on_completed_wins: _id[0] += 1 diff --git a/rx/core/operators/tofuture.py b/rx/core/operators/tofuture.py index b39a87e91..08732133b 100644 --- a/rx/core/operators/tofuture.py +++ b/rx/core/operators/tofuture.py @@ -1,3 +1,4 @@ +import asyncio from asyncio import Future from typing import Callable, Optional, TypeVar, cast @@ -11,7 +12,9 @@ def to_future_( future_ctor: Optional[Callable[[], "Future[_T]"]] = None, scheduler: Optional[abc.SchedulerBase] = None, ) -> Callable[[Observable[_T]], "Future[_T]"]: - future_ctor_: Callable[[], "Future[_T]"] = future_ctor or (lambda: Future()) + future_ctor_: Callable[[], "Future[_T]"] = ( + future_ctor or asyncio.get_event_loop().create_future + ) future: "Future[_T]" = future_ctor_() def to_future(source: Observable[_T]) -> "Future[_T]": diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 517aa1374..7c6ba0bb1 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2013,13 +2013,35 @@ def min_by( return min_by_(key_mapper, comparer) +@overload +def multicast() -> Callable[[Observable[_T]], ConnectableObservable[_T]]: + ... + + +@overload +def multicast( + subject: abc.SubjectBase[_T], +) -> Callable[[Observable[_T]], ConnectableObservable[_T]]: + ... + + +@overload +def multicast( + *, + subject_factory: Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_T]], + mapper: Optional[Callable[[Observable[_T]], Observable[_T2]]] = None, +) -> Callable[[Observable[_T]], Observable[_T2]]: + ... + + def multicast( subject: Optional[abc.SubjectBase[_T]] = None, + *, subject_factory: Optional[ Callable[[Optional[abc.SchedulerBase]], abc.SubjectBase[_T]] ] = None, - mapper: Optional[Callable[[ConnectableObservable[_T]], Observable[_T]]] = None, -) -> Callable[[Observable[_T]], Union[Observable[_T], ConnectableObservable[_T]]]: + mapper: Optional[Callable[[Observable[_T]], Observable[_T2]]] = None, +) -> Callable[[Observable[_T]], Union[Observable[_T2], ConnectableObservable[_T]]]: """Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a mapper function. Each subscription to the resulting sequence causes a @@ -2051,7 +2073,7 @@ def multicast( """ from rx.core.operators.multicast import multicast_ - return multicast_(subject, subject_factory, mapper) + return multicast_(subject, subject_factory=subject_factory, mapper=mapper) def observe_on( diff --git a/tests/test_observable/test_multicast.py b/tests/test_observable/test_multicast.py index c0183611d..ea788fb8f 100644 --- a/tests/test_observable/test_multicast.py +++ b/tests/test_observable/test_multicast.py @@ -1,7 +1,9 @@ import unittest -import pytest +from typing import Any, List, Optional from rx import operators as ops +from rx.core import abc +from rx.core.observable.connectableobservable import ConnectableObservable from rx.subject import Subject from rx.testing import TestScheduler, ReactiveTest @@ -18,7 +20,7 @@ class TestMulticast(unittest.TestCase): def test_multicast_hot_1(self): scheduler = TestScheduler() - s = Subject() + s: Subject[int] = Subject() xs = scheduler.create_hot_observable( on_next(40, 0), @@ -29,35 +31,40 @@ def test_multicast_hot_1(self): on_next(270, 5), on_next(330, 6), on_next(340, 7), - on_completed(390)) + on_completed(390), + ) obv = scheduler.create_observer() - d1 = [None] - d2 = [None] - c = [None] + d1: List[Optional[abc.DisposableBase]] = [None] + d2: List[Optional[abc.DisposableBase]] = [None] + c: List[Optional[ConnectableObservable[int]]] = [None] - def action(scheduler, state): + def action(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action) - def action0(scheduler, state): - d1[0] = c[0].subscribe(obv, scheduler) + def action0(scheduler: abc.SchedulerBase, state: Any = None): + assert c[0] + d1[0] = c[0].subscribe(obv, scheduler=scheduler) + scheduler.schedule_absolute(100, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): + assert c[0] d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(200, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): + assert d1[0] d1[0].dispose() + scheduler.schedule_absolute(300, action2) scheduler.start() - assert obv.messages == [ - on_next(210, 3), - on_next(240, 4), - on_next(270, 5)] + assert obv.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5)] assert xs.subscriptions == [subscribe(200, 390)] def test_multicast_hot_2(self): @@ -66,26 +73,37 @@ def test_multicast_hot_2(self): d2 = [None] scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(40, 0), on_next(90, 1), on_next(150, 2), - on_next(210, 3), on_next(240, 4), on_next(270, 5), - on_next(330, 6), on_next(340, 7), on_completed(390)) + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o, scheduler) + scheduler.schedule_absolute(200, action2) - def action3(scheduler, state): + def action3(scheduler: abc.SchedulerBase, state: Any = None): return d1[0].dispose() + scheduler.schedule_absolute(300, action3) scheduler.start() @@ -97,24 +115,38 @@ def test_multicast_hot_21(self): d1 = [None] d2 = [None] scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o) + scheduler.schedule_absolute(200, action2) - def action3(scheduler, state): + def action3(scheduler: abc.SchedulerBase, state: Any = None): return d1[0].dispose() + scheduler.schedule_absolute(300, action3) scheduler.start() @@ -126,88 +158,143 @@ def test_multicast_hot_3(self): d1 = [None] d2 = [None] scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o) + scheduler.schedule_absolute(200, action2) - def action3(scheduler, state): + def action3(scheduler: abc.SchedulerBase, state: Any = None): d2[0].dispose() + scheduler.schedule_absolute(300, action3) - def action4(scheduler, state): + def action4(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(335, action4) scheduler.start() - assert o.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(340, 7), on_completed(390)] + assert o.messages == [ + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(340, 7), + on_completed(390), + ] assert xs.subscriptions == [subscribe(100, 300), subscribe(335, 390)] def test_multicast_hot_4(self): c = [None] d1 = [None] d2 = [None] - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_error(390, ex)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_error(390, ex), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o, scheduler) + scheduler.schedule_absolute(200, action2) - def action3(scheduler, state): + def action3(scheduler: abc.SchedulerBase, state: Any = None): d2[0].dispose() + scheduler.schedule_absolute(300, action3) - def action4(scheduler, state): + def action4(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(335, action4) scheduler.start() - assert o.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(340, 7), on_error(390, ex)] + assert o.messages == [ + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(340, 7), + on_error(390, ex), + ] assert xs.subscriptions == [subscribe(100, 300), subscribe(335, 390)] def test_multicast_hot_5(self): c = [None] d1 = [None] d2 = [None] - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_error(390, ex)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_error(390, ex), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o, scheduler) + scheduler.schedule_absolute(400, action2) scheduler.start() @@ -218,22 +305,35 @@ def test_multicast_hot_6(self): c = [None] d1 = [None] d2 = [None] - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) s = Subject() o = scheduler.create_observer() - def action0(scheduler, state): + def action0(scheduler: abc.SchedulerBase, state: Any = None): c[0] = xs.pipe(ops.multicast(s)) + scheduler.schedule_absolute(50, action0) - def action1(scheduler, state): + def action1(scheduler: abc.SchedulerBase, state: Any = None): d2[0] = c[0].connect(scheduler) + scheduler.schedule_absolute(100, action1) - def action2(scheduler, state): + def action2(scheduler: abc.SchedulerBase, state: Any = None): d1[0] = c[0].subscribe(o, scheduler) + scheduler.schedule_absolute(400, action2) scheduler.start() @@ -242,67 +342,150 @@ def action2(scheduler, state): def test_multicast_cold_completed(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) def create(): def subject_factory(scheduler): return Subject() + def mapper(ys): return ys - return xs.pipe(ops.multicast(subject_factory=subject_factory, mapper=mapper)) + + return xs.pipe( + ops.multicast(subject_factory=subject_factory, mapper=mapper) + ) + results = scheduler.start(create) - assert results.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)] + assert results.messages == [ + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ] assert xs.subscriptions == [subscribe(200, 390)] def test_multicast_cold_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_error(390, ex)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_error(390, ex), + ) def create(): def subject_factory(scheduler): return Subject() + def mapper(ys): return ys - return xs.pipe(ops.multicast(subject_factory=subject_factory, mapper=mapper)) + + return xs.pipe( + ops.multicast(subject_factory=subject_factory, mapper=mapper) + ) results = scheduler.start(create) - assert results.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_error(390, ex)] + assert results.messages == [ + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_error(390, ex), + ] assert xs.subscriptions == [subscribe(200, 390)] def test_multicast_cold_dispose(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + ) def create(): def subject_factory(scheduler): return Subject() + def mapper(ys): return ys - return xs.pipe(ops.multicast(subject_factory=subject_factory, mapper=mapper)) + + return xs.pipe( + ops.multicast(subject_factory=subject_factory, mapper=mapper) + ) results = scheduler.start(create) - assert results.messages == [on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7)] + assert results.messages == [ + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + ] assert xs.subscriptions == [subscribe(200, 1000)] def test_multicast_cold_zip(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(40, 0), on_next(90, 1), on_next(150, 2), on_next(210, 3), on_next(240, 4), on_next(270, 5), on_next(330, 6), on_next(340, 7), on_completed(390)) + xs = scheduler.create_hot_observable( + on_next(40, 0), + on_next(90, 1), + on_next(150, 2), + on_next(210, 3), + on_next(240, 4), + on_next(270, 5), + on_next(330, 6), + on_next(340, 7), + on_completed(390), + ) def create(): def subject_factory(scheduler): return Subject() + def mapper(ys): return ys.pipe( - ops.zip(ys), - ops.map(sum), - ) + ops.zip(ys), + ops.map(sum), + ) + + return xs.pipe( + ops.multicast(subject_factory=subject_factory, mapper=mapper) + ) - return xs.pipe(ops.multicast(subject_factory=subject_factory, mapper=mapper)) results = scheduler.start(create) - assert results.messages == [on_next(210, 6), on_next(240, 8), on_next(270, 10), on_next(330, 12), on_next(340, 14), on_completed(390)] + assert results.messages == [ + on_next(210, 6), + on_next(240, 8), + on_next(270, 10), + on_next(330, 12), + on_next(340, 14), + on_completed(390), + ] assert xs.subscriptions == [subscribe(200, 390)] From 18bb1c08d3ae28e511903fba153ca14b7a211feb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 18:44:45 +0100 Subject: [PATCH 092/103] Fixed mulicast typing --- rx/core/operators/multicast.py | 5 ++++- rx/operators/__init__.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/rx/core/operators/multicast.py b/rx/core/operators/multicast.py index 0191bd238..fca4fbc1f 100644 --- a/rx/core/operators/multicast.py +++ b/rx/core/operators/multicast.py @@ -47,7 +47,7 @@ def multicast_( def multicast( source: Observable[_TSource], - ) -> Union[Observable[_TSource], ConnectableObservable[_TSource]]: + ) -> Union[Observable[_TResult], ConnectableObservable[_TSource]]: if subject_factory: def subscribe( @@ -66,6 +66,9 @@ def subscribe( return CompositeDisposable(subscription, connectable.connect(scheduler)) return Observable(subscribe) + + if not subject: + raise ValueError("multicast: Subject cannot be None") ret: ConnectableObservable[_TSource] = ConnectableObservable(source, subject) return ret diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 7c6ba0bb1..cda773d14 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -3098,8 +3098,8 @@ def starred(values: Tuple[Any, ...]) -> Any: @overload def starmap_indexed( - mapper: Callable[[_A, _B, _C, int], _T] -) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: + mapper: Callable[[_A, int], _T] +) -> Callable[[Observable[_A]], Observable[_T]]: ... @@ -3110,6 +3110,13 @@ def starmap_indexed( ... +@overload +def starmap_indexed( + mapper: Callable[[_A, _B, _C, int], _T] +) -> Callable[[Observable[Tuple[_A, _B, _C]]], Observable[_T]]: + ... + + @overload def starmap_indexed( mapper: Callable[[_A, _B, _C, _D, int], _T] From 3c897daa855de689b4d35b8f338b191b79c44b1a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 18:44:56 +0100 Subject: [PATCH 093/103] Fixed map_indexed typing --- rx/core/operators/map.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rx/core/operators/map.py b/rx/core/operators/map.py index 3f0b69c61..729ffd6e5 100644 --- a/rx/core/operators/map.py +++ b/rx/core/operators/map.py @@ -1,7 +1,7 @@ -from typing import Any, Callable, Optional, TypeVar, cast +from typing import Callable, Optional, TypeVar, cast from rx import operators as ops -from rx.core import Observable, abc, pipe +from rx.core import Observable, abc, pipe, typing from rx.core.typing import Mapper, MapperIndexed from rx.internal.basic import identity from rx.internal.utils import infinite @@ -61,9 +61,12 @@ def map_indexed( def _identity(value: _T1, _: int) -> _T2: return cast(_T2, value) - _mapper_indexed = mapper_indexed or _identity + _mapper_indexed = mapper_indexed or cast(typing.MapperIndexed[_T1, _T2], _identity) - return pipe(ops.zip_with_iterable(infinite()), ops.starmap_indexed(_mapper_indexed)) + return pipe( + ops.zip_with_iterable(infinite()), + ops.starmap_indexed(_mapper_indexed), + ) __all__ = ["map", "map_indexed"] From 558a2bf1bd08b9f349123240fcd8d1e658c55afc Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 21:24:59 +0100 Subject: [PATCH 094/103] Fixed multicast related operators and overloads --- rx/core/notification.py | 4 +-- rx/core/operators/catch.py | 7 +--- rx/core/operators/delay.py | 46 +++++++++++++------------- rx/core/operators/filter.py | 17 ++++++---- rx/core/operators/groupbyuntil.py | 8 ++--- rx/core/operators/partition.py | 5 ++- rx/core/operators/publish.py | 30 +++++++++++------ rx/core/operators/publishvalue.py | 9 +++--- rx/core/operators/reduce.py | 7 ++-- rx/core/operators/replay.py | 18 +++++++---- rx/core/operators/tomarbles.py | 3 +- rx/operators/__init__.py | 54 +++++++++++++++++++++++++++---- 12 files changed, 134 insertions(+), 74 deletions(-) diff --git a/rx/core/notification.py b/rx/core/notification.py index c3cdbe625..c8dea9344 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -110,8 +110,8 @@ def __init__(self, value: _T): super(OnNext, self).__init__() self.value: _T = value - self.has_value = True - self.kind = "N" + self.has_value: bool = True + self.kind: str = "N" def _accept( self, diff --git a/rx/core/operators/catch.py b/rx/core/operators/catch.py index b8ea2d42a..378ffc94b 100644 --- a/rx/core/operators/catch.py +++ b/rx/core/operators/catch.py @@ -67,13 +67,8 @@ def catch(source: Observable[_T]) -> Observable[_T]: """ if callable(handler): return catch_handler(source, handler) - elif isinstance(handler, abc.ObservableBase): - return rx.catch(source, handler) else: - raise TypeError( - "catch operator takes whether an Observable, " - "or a callable handler as argument." - ) + return rx.catch(source, handler) return catch diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index f6967ddbc..1abbfa2b7 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -1,8 +1,9 @@ -from datetime import datetime, timedelta -from typing import Callable, Optional, TypeVar +from datetime import datetime +from typing import Any, Callable, List, Optional, TypeVar from rx import operators as ops -from rx.core import Observable, abc, typing +from rx.core import Notification, Observable, abc, typing +from rx.core.notification import OnError from rx.disposable import ( CompositeDisposable, MultipleAssignmentDisposable, @@ -11,13 +12,9 @@ from rx.internal.constants import DELTA_ZERO from rx.scheduler import TimeoutScheduler -_T = TypeVar("_T") - +from .timestamp import Timestamp -class Timestamp(object): - def __init__(self, value, timestamp): - self.value = value - self.timestamp = timestamp +_T = TypeVar("_T") def observable_delay_timespan( @@ -38,19 +35,20 @@ def subscribe( duetime = _scheduler.to_timedelta(duetime) cancelable = SerialDisposable() - exception = [None] + exception: Optional[Exception] = None active = [False] running = [False] - queue = [] + queue: List[Timestamp[Notification[_T]]] = [] - def on_next(notification): + def on_next(notification: Timestamp[Notification[_T]]) -> None: + nonlocal exception should_run = False with source.lock: - if notification.value.kind == "E": + if isinstance(notification.value, OnError): del queue[:] queue.append(notification) - exception[0] = notification.value.exception + exception = notification.value.exception should_run = not running[0] else: queue.append( @@ -63,14 +61,14 @@ def on_next(notification): active[0] = True if should_run: - if exception[0]: - observer.on_error(exception[0]) + if exception: + observer.on_error(exception) else: mad = MultipleAssignmentDisposable() cancelable.disposable = mad - def action(scheduler, state): - if exception[0]: + def action(scheduler: abc.SchedulerBase, state: Any = None): + if exception: return with source.lock: @@ -91,12 +89,11 @@ def action(scheduler, state): if queue: should_continue = True diff = queue[0].timestamp - scheduler.now - zero = DELTA_ZERO if isinstance(diff, timedelta) else 0 - recurse_duetime = max(zero, diff) + recurse_duetime = max(DELTA_ZERO, diff) else: active[0] = False - ex = exception[0] + ex = exception running[0] = False if ex: @@ -108,9 +105,10 @@ def action(scheduler, state): mad.disposable = _scheduler.schedule_relative(duetime, action) - subscription = source.pipe(ops.materialize(), ops.timestamp()).subscribe( - on_next, scheduler=_scheduler - ) + subscription = source.pipe( + ops.materialize(), + ops.timestamp(), + ).subscribe(on_next, scheduler=_scheduler) return CompositeDisposable(subscription, cancelable) diff --git a/rx/core/operators/filter.py b/rx/core/operators/filter.py index 57f39878d..491075d29 100644 --- a/rx/core/operators/filter.py +++ b/rx/core/operators/filter.py @@ -72,13 +72,16 @@ def subscribe( def on_next(value: _T): nonlocal count - try: - should_run = predicate_indexed(value, count) - except Exception as ex: # pylint: disable=broad-except - observer.on_error(ex) - return - else: - count += 1 + should_run = True + + if predicate_indexed: + try: + should_run = predicate_indexed(value, count) + except Exception as ex: # pylint: disable=broad-except + observer.on_error(ex) + return + else: + count += 1 if should_run: observer.on_next(value) diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index 675f97b4a..8c645ea7d 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import Any, Callable, Optional, Type, TypeVar +from typing import Any, Callable, Optional, Type, TypeVar, cast from rx import operators as ops from rx.core import GroupedObservable, Observable, abc @@ -48,9 +48,9 @@ def group_by_until_( encountered. """ - element_mapper = element_mapper or identity + element_mapper = element_mapper or cast(Mapper[_T, _TValue], identity) - default_subject_mapper: Callable[[], Subject[_T]] = lambda: Subject() + default_subject_mapper: Callable[[], Subject[_TValue]] = lambda: Subject() subject_mapper = subject_mapper or default_subject_mapper def group_by_until( @@ -60,7 +60,7 @@ def subscribe( observer: abc.ObserverBase[GroupedObservable[_TKey, _TValue]], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - writers: OrderedDict[_TKey, abc.ObserverBase[_TValue]] = OrderedDict() + writers: OrderedDict[_TKey, Subject[_TValue]] = OrderedDict() group_disposable = CompositeDisposable() ref_count_disposable = RefCountDisposable(group_disposable) diff --git a/rx/core/operators/partition.py b/rx/core/operators/partition.py index 81ea2085f..b10829fb4 100644 --- a/rx/core/operators/partition.py +++ b/rx/core/operators/partition.py @@ -31,7 +31,10 @@ def partition(source: Observable[_T]) -> List[Observable[_T]]: predicate returns False. """ - published = source.pipe(ops.publish(), ops.ref_count()) + published = source.pipe( + ops.publish(), + ops.ref_count(), + ) return [ published.pipe(ops.filter(predicate)), published.pipe(ops.filter(lambda x: not predicate(x))), diff --git a/rx/core/operators/publish.py b/rx/core/operators/publish.py index 3fb78d777..94c181d76 100644 --- a/rx/core/operators/publish.py +++ b/rx/core/operators/publish.py @@ -1,17 +1,19 @@ -from typing import Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, Union from rx import operators as ops -from rx.core import ConnectableObservable, Observable, pipe +from rx.core import ConnectableObservable, Observable, abc, pipe from rx.core.typing import Mapper from rx.subject import Subject -_T = TypeVar("_T") -_T2 = TypeVar("_T2") +_TSource = TypeVar("_TSource") +_TResult = TypeVar("_TResult") def publish_( - mapper: Optional[Mapper[_T, _T2]] = None, -) -> Callable[[Observable[_T]], ConnectableObservable[_T2]]: + mapper: Optional[Mapper[Observable[_TSource], Observable[_TResult]]] = None, +) -> Callable[ + [Observable[_TSource]], Union[Observable[_TResult], ConnectableObservable[_TSource]] +]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence. This operator is a @@ -34,12 +36,17 @@ def publish_( """ if mapper: - return pipe(ops.multicast(subject_factory=lambda _: Subject(), mapper=mapper)) - return pipe(ops.multicast(subject=Subject())) + def factory(scheduler: Optional[abc.SchedulerBase] = None) -> Subject[_TSource]: + return Subject() + return ops.multicast(subject_factory=factory, mapper=mapper) -def share_() -> Callable[[Observable[_T]], Observable[_T]]: + subject: Subject[_TSource] = Subject() + return ops.multicast(subject=subject) + + +def share_() -> Callable[[Observable[_TSource]], Observable[_TSource]]: """Share a single subscription among multple observers. Returns a new Observable that multicasts (shares) the original @@ -50,7 +57,10 @@ def share_() -> Callable[[Observable[_T]], Observable[_T]]: This is an alias for a composed publish() and ref_count(). """ - return pipe(publish_(), ops.ref_count()) + return pipe( + ops.publish(), + ops.ref_count(), + ) __all__ = ["publish_", "share_"] diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index e201ba745..94d06127d 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -1,7 +1,7 @@ -from typing import Callable, Optional, TypeVar, cast +from typing import Callable, Optional, TypeVar, cast, Union from rx import operators as ops -from rx.core import Observable, abc +from rx.core import Observable, abc, ConnectableObservable from rx.core.typing import Mapper from rx.subject import BehaviorSubject @@ -10,8 +10,9 @@ def publish_value_( - initial_value: _T1, mapper: Optional[Mapper[_T1, _T2]] = None -) -> Callable[[Observable[_T1]], Observable[_T2]]: + initial_value: _T1, + mapper: Optional[Mapper[Observable[_T1], Observable[_T2]]] = None, +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T1]]]: if mapper: def subject_factory( diff --git a/rx/core/operators/reduce.py b/rx/core/operators/reduce.py index 0780659b9..6e3f0a3c5 100644 --- a/rx/core/operators/reduce.py +++ b/rx/core/operators/reduce.py @@ -11,7 +11,7 @@ def reduce_( accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet -) -> Callable[[Observable[_T]], Observable[_TState]]: +) -> Callable[[Observable[_T]], Observable[Any]]: """Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified seed value is used as the initial @@ -42,7 +42,10 @@ def reduce_( ops.last_or_default(default_value=seed_), ) - return pipe(ops.scan(accumulator), ops.last()) + return pipe( + ops.scan(accumulator), + ops.last(), + ) __all__ = ["reduce_"] diff --git a/rx/core/operators/replay.py b/rx/core/operators/replay.py index a7fd921d1..dffa86549 100644 --- a/rx/core/operators/replay.py +++ b/rx/core/operators/replay.py @@ -5,16 +5,18 @@ from rx.core.typing import Mapper from rx.subject import ReplaySubject -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") +_TSource = TypeVar("_TSource") +_TResult = TypeVar("_TResult") def replay_( - mapper: Optional[Mapper[_T1, _T2]] = None, + mapper: Optional[Mapper[Observable[_TSource], Observable[_TResult]]] = None, buffer_size: Optional[int] = None, window: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T2]]]: +) -> Callable[ + [Observable[_TSource]], Union[Observable[_TResult], ConnectableObservable[_TSource]] +]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence replaying notifications @@ -48,12 +50,14 @@ def replay_( if mapper: - def subject_factory(scheduler: abc.SchedulerBase) -> ReplaySubject[_T2]: + def subject_factory( + scheduler: Optional[abc.SchedulerBase] = None, + ) -> ReplaySubject[_TSource]: return ReplaySubject(buffer_size, window, scheduler) return ops.multicast(subject_factory=subject_factory, mapper=mapper) - - return ops.multicast(ReplaySubject(buffer_size, window, scheduler)) + rs: ReplaySubject[_TSource] = ReplaySubject(buffer_size, window, scheduler) + return ops.multicast(rs) __all__ = ["replay_"] diff --git a/rx/core/operators/tomarbles.py b/rx/core/operators/tomarbles.py index 97df06a53..bfa8e06e6 100644 --- a/rx/core/operators/tomarbles.py +++ b/rx/core/operators/tomarbles.py @@ -39,7 +39,8 @@ def add_timespan(): diff = now - last last = now secs = scheduler.to_seconds(diff) - dashes = "-" * int((secs + timespan / 2.0) * (1.0 / timespan)) + timespan_ = scheduler.to_seconds(timespan) + dashes = "-" * int((secs + timespan_ / 2.0) * (1.0 / timespan_)) result.append(dashes) def on_next(value: Any) -> None: diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index cda773d14..18ad93ee3 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2256,9 +2256,21 @@ def pluck_attr(prop: str) -> Callable[[Observable[Any]], Observable[Any]]: return pluck_attr_(prop) +@overload +def publish() -> Callable[[Observable[_T1]], ConnectableObservable[_T1]]: + ... + + +@overload def publish( - mapper: Optional[Mapper[_T1, _T2]] = None, -) -> Callable[[Observable[_T1]], ConnectableObservable[_T2]]: + mapper: Mapper[Observable[_T1], Observable[_T2]], +) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +def publish( + mapper: Optional[Mapper[Observable[_T1], Observable[_T2]]] = None, +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T1]]]: """The `publish` operator. Returns an observable sequence that is the result of invoking the @@ -2289,9 +2301,25 @@ def publish( return publish_(mapper) +@overload +def publish_value( + initial_value: _T1, +) -> Callable[[Observable[_T1]], ConnectableObservable[_T1]]: + ... + + +@overload def publish_value( - initial_value: _T1, mapper: Optional[Mapper[_T1, _T2]] = None + initial_value: _T1, + mapper: Mapper[Observable[_T1], Observable[_T2]], ) -> Callable[[Observable[_T1]], Observable[_T2]]: + ... + + +def publish_value( + initial_value: _T1, + mapper: Optional[Mapper[Observable[_T1], Observable[_T2]]] = None, +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T1]]]: """Returns an observable sequence that is the result of invoking the mapper on a connectable observable sequence that shares a single subscription to the underlying sequence and starts with @@ -2326,9 +2354,23 @@ def publish_value( return publish_value_(initial_value, mapper) +@overload def reduce( - accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet + accumulator: Accumulator[_TState, _T] +) -> Callable[[Observable[_T]], Observable[_T]]: + ... + + +@overload +def reduce( + accumulator: Accumulator[_TState, _T], seed: _TState ) -> Callable[[Observable[_T]], Observable[_TState]]: + ... + + +def reduce( + accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet +) -> Callable[[Observable[_T]], Observable[Union[_T, _TState]]]: """The reduce operator. Applies an accumulator function over an observable sequence, @@ -2408,11 +2450,11 @@ def repeat( def replay( - mapper: Optional[Mapper[_T1, _T2]] = None, + mapper: Optional[Mapper[Observable[_T1], Observable[_T2]]] = None, buffer_size: Optional[int] = None, window: Optional[typing.RelativeTime] = None, scheduler: Optional[abc.SchedulerBase] = None, -) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T2]]]: +) -> Callable[[Observable[_T1]], Union[Observable[_T2], ConnectableObservable[_T1]]]: """The `replay` operator. Returns an observable sequence that is the result of invoking the From 34840fe8bcacd970053102e3fbb47dcd0ea104cb Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 21:29:15 +0100 Subject: [PATCH 095/103] Sort imports --- rx/core/operators/publishvalue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rx/core/operators/publishvalue.py b/rx/core/operators/publishvalue.py index 94d06127d..6d529470e 100644 --- a/rx/core/operators/publishvalue.py +++ b/rx/core/operators/publishvalue.py @@ -1,7 +1,7 @@ -from typing import Callable, Optional, TypeVar, cast, Union +from typing import Callable, Optional, TypeVar, Union, cast from rx import operators as ops -from rx.core import Observable, abc, ConnectableObservable +from rx.core import ConnectableObservable, Observable, abc from rx.core.typing import Mapper from rx.subject import BehaviorSubject From abfd6bbeb519ff3c1aa9da73cee7ba2cff16480a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 14 Feb 2022 21:36:41 +0100 Subject: [PATCH 096/103] Fix typing issue with overload Make main function more generic --- rx/operators/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index 18ad93ee3..ebeecaa33 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -2370,7 +2370,7 @@ def reduce( def reduce( accumulator: Accumulator[_TState, _T], seed: Union[_TState, Type[NotSet]] = NotSet -) -> Callable[[Observable[_T]], Observable[Union[_T, _TState]]]: +) -> Callable[[Observable[_T]], Observable[Any]]: """The reduce operator. Applies an accumulator function over an observable sequence, From bb4d4216bd57f2dbe8010d4cf433b5cfbb9c9f0a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 18 Feb 2022 07:50:31 +0100 Subject: [PATCH 097/103] Pyright type checks at basic settings --- .github/workflows/pythonpackage.yml | 2 +- rx/core/notification.py | 6 +- rx/core/operators/all.py | 13 +- rx/core/operators/count.py | 6 +- rx/core/operators/delay.py | 8 +- rx/core/operators/distinctuntilchanged.py | 8 +- rx/core/operators/groupbyuntil.py | 8 +- rx/core/operators/isempty.py | 8 +- rx/core/operators/sequenceequal.py | 11 +- rx/core/operators/singleordefault.py | 20 +- rx/core/operators/skipwhile.py | 17 +- rx/core/operators/windowwithcount.py | 9 +- rx/testing/__init__.py | 10 + rx/testing/coldobservable.py | 4 +- rx/testing/hotobservable.py | 2 + rx/testing/marbles.py | 27 ++- rx/testing/recorded.py | 18 +- tests/test_core/test_notification.py | 4 +- tests/test_core/test_observer.py | 33 ++- tests/test_observable/test_asobservable.py | 29 ++- tests/test_observable/test_catch.py | 97 +++++--- .../test_distinctuntilchanged.py | 215 ++++++++++++++---- tests/test_observable/test_finally.py | 30 ++- tests/test_observable/test_materialize.py | 74 ++++-- .../test_observable/test_onerrorresumenext.py | 69 +++--- tests/test_observable/test_scan.py | 4 +- 26 files changed, 483 insertions(+), 249 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 8ee952bc8..1520ff8c0 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -25,8 +25,8 @@ jobs: isort --check rx - name: Check type annotations (mypy & pyright) run: | + pyright rx mypy rx/__init__.py rx/operators rx/core/abc rx/core/observer rx/core/observable rx/disposable rx/internal rx/subject rx/scheduler - pyright rx/__init__.py rx/operators rx/core/abc rx/core/observer rx/core/observable rx/disposable rx/internal rx/subject rx/scheduler - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/rx/core/notification.py b/rx/core/notification.py index c8dea9344..e236576e5 100644 --- a/rx/core/notification.py +++ b/rx/core/notification.py @@ -125,7 +125,7 @@ def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None: return observer.on_next(self.value) def __str__(self) -> str: - val = self.value + val: Any = self.value if isinstance(val, int): val = float(val) return "OnNext(%s)" % str(val) @@ -138,7 +138,9 @@ def __init__(self, error: Union[Exception, str]): """Constructs a notification of an exception.""" super(OnError, self).__init__() - self.exception: Union[Exception, str] = error + self.exception: Exception = ( + error if isinstance(error, Exception) else Exception(error) + ) self.kind = "E" def _accept( diff --git a/rx/core/operators/all.py b/rx/core/operators/all.py index 8e87f8e3c..f6fbb5b31 100644 --- a/rx/core/operators/all.py +++ b/rx/core/operators/all.py @@ -8,12 +8,17 @@ def all_(predicate: Predicate[_T]) -> Callable[[Observable[_T]], Observable[bool]]: + def filter(v: _T): + return not predicate(v) - filtering = ops.filter(lambda v: not predicate(v)) - mapping = ops.map(lambda b: not b) - some = ops.some() + def mapping(b: bool) -> bool: + return not b - return pipe(filtering, some, mapping) + return pipe( + ops.filter(filter), + ops.some(), + ops.map(mapping), + ) __all__ = ["all_"] diff --git a/rx/core/operators/count.py b/rx/core/operators/count.py index 04e7c1457..e04887459 100644 --- a/rx/core/operators/count.py +++ b/rx/core/operators/count.py @@ -12,8 +12,10 @@ def count_( ) -> Callable[[Observable[_T]], Observable[int]]: if predicate: - filtering = ops.filter(predicate) - return pipe(filtering, ops.count()) + return pipe( + ops.filter(predicate), + ops.count(), + ) def reducer(n: int, _: _T) -> int: return n + 1 diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 1abbfa2b7..0418dc41a 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -30,9 +30,9 @@ def subscribe( _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() if isinstance(duetime, datetime): - duetime = _scheduler.to_datetime(duetime) - _scheduler.now + duetime_ = _scheduler.to_datetime(duetime) - _scheduler.now else: - duetime = _scheduler.to_timedelta(duetime) + duetime_ = _scheduler.to_timedelta(duetime) cancelable = SerialDisposable() exception: Optional[Exception] = None @@ -54,7 +54,7 @@ def on_next(notification: Timestamp[Notification[_T]]) -> None: queue.append( Timestamp( value=notification.value, - timestamp=notification.timestamp + duetime, + timestamp=notification.timestamp + duetime_, ) ) should_run = not active[0] @@ -103,7 +103,7 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): recurse_duetime, action ) - mad.disposable = _scheduler.schedule_relative(duetime, action) + mad.disposable = _scheduler.schedule_relative(duetime_, action) subscription = source.pipe( ops.materialize(), diff --git a/rx/core/operators/distinctuntilchanged.py b/rx/core/operators/distinctuntilchanged.py index 69e5b7362..45b5fc724 100644 --- a/rx/core/operators/distinctuntilchanged.py +++ b/rx/core/operators/distinctuntilchanged.py @@ -13,8 +13,8 @@ def distinct_until_changed_( comparer: Optional[Comparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: - key_mapper = key_mapper or cast(Callable[[_T], _TKey], identity) - comparer = comparer or default_comparer + key_mapper_ = key_mapper or cast(Callable[[_T], _TKey], identity) + comparer_ = comparer or default_comparer def distinct_until_changed(source: Observable[_T]) -> Observable[_T]: """Returns an observable sequence that contains only distinct @@ -51,14 +51,14 @@ def on_next(value: _T) -> None: nonlocal has_current_key, current_key comparer_equals = False try: - key = key_mapper(value) + key = key_mapper_(value) except Exception as exception: # pylint: disable=broad-except observer.on_error(exception) return if has_current_key: try: - comparer_equals = comparer(current_key, key) + comparer_equals = comparer_(current_key, key) except Exception as exception: # pylint: disable=broad-except observer.on_error(exception) return diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index 8c645ea7d..dec6f00f1 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -48,10 +48,10 @@ def group_by_until_( encountered. """ - element_mapper = element_mapper or cast(Mapper[_T, _TValue], identity) + element_mapper_ = element_mapper or cast(Mapper[_T, _TValue], identity) default_subject_mapper: Callable[[], Subject[_TValue]] = lambda: Subject() - subject_mapper = subject_mapper or default_subject_mapper + subject_mapper_ = subject_mapper or default_subject_mapper def group_by_until( source: Observable[_T], @@ -81,7 +81,7 @@ def on_next(x: _T) -> None: writer = writers.get(key) if not writer: try: - writer = subject_mapper() + writer = subject_mapper_() except Exception as e: for wrt in writers.values(): wrt.on_error(e) @@ -135,7 +135,7 @@ def on_completed(): ) try: - element = element_mapper(x) + element = element_mapper_(x) except Exception as error: for wrt in writers.values(): wrt.on_error(error) diff --git a/rx/core/operators/isempty.py b/rx/core/operators/isempty.py index 07e65a196..f8bf4e17b 100644 --- a/rx/core/operators/isempty.py +++ b/rx/core/operators/isempty.py @@ -12,7 +12,13 @@ def is_empty_() -> Callable[[Observable[Any]], Observable[bool]]: determining whether the source sequence is empty. """ - return pipe(ops.some(), ops.map(lambda b: not b)) + def mapper(b: bool) -> bool: + return not b + + return pipe( + ops.some(), + ops.map(mapper), + ) __all__ = ["is_empty_"] diff --git a/rx/core/operators/sequenceequal.py b/rx/core/operators/sequenceequal.py index 12f6c885f..f7ca2ec5f 100644 --- a/rx/core/operators/sequenceequal.py +++ b/rx/core/operators/sequenceequal.py @@ -12,9 +12,8 @@ def sequence_equal_( second: Union[Observable[_T], Iterable[_T]], comparer: Optional[typing.Comparer[_T]] = None, ) -> Callable[[Observable[_T]], Observable[bool]]: - comparer = comparer or default_comparer - if isinstance(second, Iterable): - second = rx.from_iterable(second) + comparer_ = comparer or default_comparer + second_ = rx.from_iterable(second) if isinstance(second, Iterable) else second def sequence_equal(source: Observable[_T]) -> Observable[bool]: """Determines whether two sequences are equal by comparing the @@ -50,7 +49,7 @@ def on_next1(x: _T) -> None: if len(qr) > 0: v = qr.pop(0) try: - equal = comparer(v, x) + equal = comparer_(v, x) except Exception as e: observer.on_error(e) return @@ -79,7 +78,7 @@ def on_next2(x: _T): if len(ql) > 0: v = ql.pop(0) try: - equal = comparer(v, x) + equal = comparer_(v, x) except Exception as exception: observer.on_error(exception) return @@ -107,7 +106,7 @@ def on_completed2(): subscription1 = first.subscribe( on_next1, observer.on_error, on_completed1, scheduler=scheduler ) - subscription2 = second.subscribe( + subscription2 = second_.subscribe( on_next2, observer.on_error, on_completed2, scheduler=scheduler ) return CompositeDisposable(subscription1, subscription2) diff --git a/rx/core/operators/singleordefault.py b/rx/core/operators/singleordefault.py index d3969308c..997f3877a 100644 --- a/rx/core/operators/singleordefault.py +++ b/rx/core/operators/singleordefault.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, cast from rx import operators as ops from rx.core import Observable, abc, pipe @@ -9,30 +9,32 @@ def single_or_default_async_( - has_default: bool = False, default_value: _T = None + has_default: bool = False, default_value: Optional[_T] = None ) -> Callable[[Observable[_T]], Observable[_T]]: def single_or_default_async(source: Observable[_T]) -> Observable[_T]: def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None, ): - value = [default_value] - seen_value = [False] + value = cast(_T, default_value) + seen_value = False def on_next(x: _T): - if seen_value[0]: + nonlocal value, seen_value + + if seen_value: observer.on_error( Exception("Sequence contains more than one element") ) else: - value[0] = x - seen_value[0] = True + value = x + seen_value = True def on_completed(): - if not seen_value[0] and not has_default: + if not seen_value and not has_default: observer.on_error(SequenceContainsNoElementsError()) else: - observer.on_next(value[0]) + observer.on_next(value) observer.on_completed() return source.subscribe( diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index baa5661c9..8b90f44d1 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar +from typing import Callable, Optional, TypeVar, Tuple from rx import operators as ops from rx.core import Observable, abc, pipe, typing @@ -58,10 +58,19 @@ def on_next(value: _T): def skip_while_indexed_( predicate: typing.PredicateIndexed[_T], ) -> Callable[[Observable[_T]], Observable[_T]]: + def indexer(x: _T, i: int) -> Tuple[_T, int]: + return (x, i) + + def skipper(x: Tuple[_T, int]) -> bool: + return predicate(*x) + + def mapper(x: Tuple[_T, int]) -> _T: + return x[0] + return pipe( - ops.map_indexed(lambda x, i: (x, i)), - ops.skip_while(lambda x: predicate(*x)), - ops.map(lambda x: x[0]), + ops.map_indexed(indexer), + ops.skip_while(skipper), + ops.map(mapper), ) diff --git a/rx/core/operators/windowwithcount.py b/rx/core/operators/windowwithcount.py index b7fead1f8..8c4246a3e 100644 --- a/rx/core/operators/windowwithcount.py +++ b/rx/core/operators/windowwithcount.py @@ -35,10 +35,9 @@ def window_with_count_( if count <= 0: raise ArgumentOutOfRangeException() - if skip is None: - skip = count + skip_ = skip if skip is not None else count - if skip <= 0: + if skip_ <= 0: raise ArgumentOutOfRangeException() def window_with_count(source: Observable[_T]) -> Observable[Observable[_T]]: @@ -63,12 +62,12 @@ def on_next(x: _T) -> None: item.on_next(x) c = n[0] - count + 1 - if c >= 0 and c % skip == 0: + if c >= 0 and c % skip_ == 0: s = q.pop(0) s.on_completed() n[0] += 1 - if (n[0] % skip) == 0: + if (n[0] % skip_) == 0: create_window() def on_error(exception: Exception) -> None: diff --git a/rx/testing/__init__.py b/rx/testing/__init__.py index 185a49ae8..bf8af0b6f 100644 --- a/rx/testing/__init__.py +++ b/rx/testing/__init__.py @@ -2,3 +2,13 @@ from .reactivetest import OnErrorPredicate, OnNextPredicate, ReactiveTest, is_prime from .recorded import Recorded from .testscheduler import TestScheduler + +__all__ = [ + "MockDisposable", + "OnErrorPredicate", + "OnNextPredicate", + "ReactiveTest", + "Recorded", + "TestScheduler", + "is_prime", +] diff --git a/rx/testing/coldobservable.py b/rx/testing/coldobservable.py index 4e496650d..067e84420 100644 --- a/rx/testing/coldobservable.py +++ b/rx/testing/coldobservable.py @@ -41,6 +41,8 @@ def action( for message in self.messages: notification = message.value + if not isinstance(notification, Notification): + raise ValueError("Must be notification") # Don't make closures within a loop action = get_action(notification) @@ -49,7 +51,7 @@ def action( def dispose() -> None: start = self.subscriptions[index].subscribe end = self.scheduler.to_seconds(self.scheduler.now) - self.subscriptions[index] = Subscription(start, end) + self.subscriptions[index] = Subscription(start, int(end)) disp.dispose() return Disposable(dispose) diff --git a/rx/testing/hotobservable.py b/rx/testing/hotobservable.py index 4d33acf9c..a4696f1db 100644 --- a/rx/testing/hotobservable.py +++ b/rx/testing/hotobservable.py @@ -34,6 +34,8 @@ def action(scheduler: abc.SchedulerBase, state: Any): for message in self.messages: notification = message.value + if not isinstance(notification, Notification): + raise ValueError("Must be notification") # Warning: Don't make closures within a loop action = get_action(notification) diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index bb3d9ff03..834acea44 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -1,11 +1,13 @@ from collections import namedtuple from contextlib import contextmanager +from datetime import timedelta from typing import Any, Dict, List, Optional, Tuple, Union from warnings import warn import rx -from rx.core import Observable -from rx.core.notification import Notification +from rx.core import Observable, typing +from rx.core.abc.scheduler import SchedulerBase +from rx.core.notification import Notification, OnNext, OnError from rx.core.observable.marbles import parse from rx.core.typing import Callable, RelativeTime from rx.scheduler import NewThreadScheduler @@ -153,7 +155,7 @@ def test_hot( def messages_to_records( - messages: List[Tuple[RelativeTime, Notification[Any]]] + messages: List[Tuple[typing.RelativeTime, Notification[Any]]] ) -> List[Recorded[Any]]: """ Helper function to convert messages returned by parse() to a list of @@ -161,16 +163,19 @@ def messages_to_records( """ records: List[Recorded[Any]] = [] - dispatcher: Dict[str, Callable[[int, Notification[Any]], Recorded[Any]]] = { - "N": lambda t, n: ReactiveTest.on_next(t, n.value), - "E": lambda t, n: ReactiveTest.on_error(t, n.exception), - "C": lambda t, n: ReactiveTest.on_completed(t), - } - for message in messages: time, notification = message - kind = notification.kind - record = dispatcher[kind](time, notification) + if isinstance(time, float): + time_ = int(time) + else: + time_ = time.microseconds // 1000 + + if isinstance(notification, OnNext): + record = ReactiveTest.on_next(time_, notification.value) + elif isinstance(notification, OnError): + record = ReactiveTest.on_error(time_, notification.exception) + else: + record = ReactiveTest.on_completed(time_) records.append(record) return records diff --git a/rx/testing/recorded.py b/rx/testing/recorded.py index 2b0d0b525..c5e21bea5 100644 --- a/rx/testing/recorded.py +++ b/rx/testing/recorded.py @@ -1,7 +1,6 @@ -from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast -from rx.core import Notification, typing -from rx.internal.basic import default_comparer +from rx.core import Notification if TYPE_CHECKING: from .reactivetest import OnErrorPredicate, OnNextPredicate @@ -15,20 +14,21 @@ def __init__( self, time: int, value: Union[Notification[_T], "OnNextPredicate[_T]", "OnErrorPredicate[_T]"], - comparer: Optional[typing.Comparer[_T]] = None, + # comparer: Optional[typing.Comparer[_T]] = None, ): self.time = time self.value = value - self.comparer = comparer or default_comparer + # self.comparer = comparer or default_comparer def __eq__(self, other: Any) -> bool: """Returns true if a recorded value matches another recorded value""" if isinstance(other, Recorded): - time_match = self.time == other.time - return time_match and self.comparer( - self.value, cast(Recorded[_T], other).value - ) + other_ = cast(Recorded[_T], other) + time_match = self.time == other_.time + if not time_match: + return False + return self.value == other_.value return False diff --git a/tests/test_core/test_notification.py b/tests/test_core/test_notification.py index 069c559ba..5ad7b3160 100644 --- a/tests/test_core/test_notification.py +++ b/tests/test_core/test_notification.py @@ -140,7 +140,7 @@ def test_throw_ctor_and_props(): n = OnError(e) assert "E" == n.kind assert not n.has_value - assert e == n.exception + assert e == str(n.exception) def test_throw_equality(): @@ -177,7 +177,7 @@ def on_next(self, value: Any) -> None: raise NotImplementedError() def on_error(self, error: Exception) -> None: - self.error = error + self.error = str(error) def on_completed(self) -> None: raise NotImplementedError() diff --git a/tests/test_core/test_observer.py b/tests/test_core/test_observer.py index e5d0bd6fb..3d52dd2bb 100644 --- a/tests/test_core/test_observer.py +++ b/tests/test_core/test_observer.py @@ -3,7 +3,6 @@ class MyObserver(Observer): - def __init__(self): super().__init__() self.has_on_next = None @@ -13,8 +12,8 @@ def __init__(self): def _on_next_core(self, value): self.has_on_next = value - def _on_error_core(self, error): - self.has_on_error = error + def _on_error_core(self, error: Exception): + self.has_on_error = str(error) def _on_completed_core(self): self.has_on_completed = True @@ -25,7 +24,7 @@ def test_to_observer_notification_on_next(): def next(n): assert i == 0 - assert n.kind == 'N' + assert n.kind == "N" assert n.value == 42 assert not hasattr(n, "exception") assert n.has_value @@ -34,13 +33,13 @@ def next(n): def test_to_observer_notification_on_error(): - ex = 'ex' + ex = "ex" i = 0 def next(n): assert i == 0 - assert n.kind == 'E' - assert n.exception == ex + assert n.kind == "E" + assert str(n.exception) == ex assert not n.has_value from_notifier(next).on_error(ex) @@ -51,7 +50,7 @@ def test_to_observer_notification_completed(): def next(n): assert i == 0 - assert n.kind == 'C' + assert n.kind == "C" assert not n.has_value from_notifier(next).on_completed() @@ -62,7 +61,7 @@ def test_to_notifier_forwards(): obsn.to_notifier()(OnNext(42)) assert obsn.has_on_next == 42 - ex = 'ex' + ex = "ex" obse = MyObserver() obse.to_notifier()(OnError(ex)) assert ex == obse.has_on_error @@ -87,7 +86,7 @@ def on_next(x): def test_create_on_next_has_error(): - ex = 'ex' + ex = "ex" next = [False] _e = None @@ -136,7 +135,7 @@ def on_completed(): def test_create_on_next_close_has_error(): e_ = None - ex = 'ex' + ex = "ex" next = [False] completed = [False] @@ -163,7 +162,7 @@ def on_completed(): def test_create_on_next_on_error(): - ex = 'ex' + ex = "ex" next = [True] error = [False] @@ -187,7 +186,7 @@ def on_error(e): def test_create_on_next_throw_hit_completed(): - ex = 'ex' + ex = "ex" next = [True] error = [False] @@ -211,7 +210,7 @@ def on_error(e): def test_create_on_next_throw_close1(): - ex = 'ex' + ex = "ex" next = [True] error = [False] completed = [False] @@ -242,7 +241,7 @@ def on_completed(): def test_create_on_next_throw_close2(): - ex = 'ex' + ex = "ex" next = [True] error = [False] completed = [False] @@ -285,7 +284,7 @@ def test_as_observer_forwards(): obsn.as_observer().on_next(42) assert obsn.has_on_next == 42 - ex = 'ex' + ex = "ex" obse = MyObserver() obse.as_observer().on_error(ex) assert obse.has_on_error == ex @@ -295,5 +294,5 @@ def test_as_observer_forwards(): assert obsc.has_on_completed -if __name__ == '__main__': +if __name__ == "__main__": test_to_notifier_forwards() diff --git a/tests/test_observable/test_asobservable.py b/tests/test_observable/test_asobservable.py index 713672646..ca17cca20 100644 --- a/tests/test_observable/test_asobservable.py +++ b/tests/test_observable/test_asobservable.py @@ -23,6 +23,7 @@ def test_as_observable_never(self): def create(): return rx.never().pipe(ops.as_observable()) + results = scheduler.start(create) assert results.messages == [] @@ -36,10 +37,10 @@ def create(): results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'C' and results[0].time == 250) + assert results[0].value.kind == "C" and results[0].time == 250 def test_as_observable_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) @@ -48,19 +49,29 @@ def create(): results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'E' and results[0].value.exception == ex and results[0].time == 250) + assert ( + results[0].value.kind == "E" + and str(results[0].value.exception) == ex + and results[0].time == 250 + ) def test_as_observable_Return(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(220, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(220, 2), on_completed(250) + ) def create(): return xs.pipe(ops.as_observable()) results = scheduler.start(create).messages self.assertEqual(2, len(results)) - assert results[0].value.kind == 'N' and results[0].value.value == 2 and results[0].time == 220 - assert results[1].value.kind == 'C' and results[1].time == 250 + assert ( + results[0].value.kind == "N" + and results[0].value.value == 2 + and results[0].time == 220 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_as_observable_isnoteager(self): scheduler = TestScheduler() @@ -68,10 +79,13 @@ def test_as_observable_isnoteager(self): def subscribe(obs, scheduler=None): subscribed[0] = True - disp = scheduler.create_hot_observable(on_next(150, 1), on_next(220, 2), on_completed(250)).subscribe(obs) + disp = scheduler.create_hot_observable( + on_next(150, 1), on_next(220, 2), on_completed(250) + ).subscribe(obs) def func(): return disp.dispose() + return func xs = rx.create(subscribe) @@ -80,6 +94,7 @@ def func(): def create(): return xs.pipe(ops.as_observable()) + scheduler.start(create) assert subscribed[0] diff --git a/tests/test_observable/test_catch.py b/tests/test_observable/test_catch.py index 1417eb7c4..15f6385ed 100644 --- a/tests/test_observable/test_catch.py +++ b/tests/test_observable/test_catch.py @@ -14,7 +14,6 @@ class TestCatch(unittest.TestCase): - def test_catch_no_errors(self): scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_completed(230)] @@ -24,6 +23,7 @@ def test_catch_no_errors(self): def create(): return o1.pipe(ops.catch(o2)) + results = scheduler.start(create) assert results.messages == [on_next(210, 2), on_next(220, 3), on_completed(230)] @@ -36,6 +36,7 @@ def test_catch_never(self): def create(): return o1.pipe(ops.catch(o2)) + results = scheduler.start(create) assert results.messages == [] @@ -67,7 +68,7 @@ def create(): assert results.messages == [on_next(210, 2), on_completed(230)] def test_catch_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, ex)] msgs2 = [on_next(240, 5), on_completed(250)] @@ -78,14 +79,20 @@ def create(): return o1.pipe(ops.catch(o2)) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_next(240, 5), on_completed(250)] + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(240, 5), + on_completed(250), + ] def test_catch_error_never(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, ex)] o1 = scheduler.create_hot_observable(msgs1) o2 = rx.never() + def create(): return o1.pipe(ops.catch(o2)) @@ -93,9 +100,14 @@ def create(): assert results.messages == [on_next(210, 2), on_next(220, 3)] def test_catch_error_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, 'ex1')] + msgs1 = [ + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_error(230, "ex1"), + ] msgs2 = [on_next(240, 4), on_error(250, ex)] o1 = scheduler.create_hot_observable(msgs1) o2 = scheduler.create_hot_observable(msgs2) @@ -104,10 +116,15 @@ def create(): return o1.pipe(ops.catch(o2)) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_next(240, 4), on_error(250, ex)] + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(240, 4), + on_error(250, ex), + ] def test_catch_multiple(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_error(215, ex)] msgs2 = [on_next(220, 3), on_error(225, ex)] @@ -120,10 +137,15 @@ def create(): return rx.catch(o1, o2, o3) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_next(230, 4), on_completed(235)] + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_completed(235), + ] def test_catch_error_specific_caught(self): - ex = 'ex' + ex = "ex" handler_called = [False] scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, ex)] @@ -140,11 +162,16 @@ def handler(e, source): results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_next(240, 4), on_completed(250)] + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(240, 4), + on_completed(250), + ] assert handler_called[0] def test_catch_error_specific_caught_immediate(self): - ex = 'ex' + ex = "ex" handler_called = [False] scheduler = TestScheduler() msgs2 = [on_next(240, 4), on_completed(250)] @@ -155,16 +182,16 @@ def handler(e, source): handler_called[0] = True return o2 - return rx.throw('ex').pipe(ops.catch(handler)) + return rx.throw("ex").pipe(ops.catch(handler)) results = scheduler.start(create) assert results.messages == [on_next(240, 4), on_completed(250)] - assert(handler_called[0]) + assert handler_called[0] def test_catch_handler_throws(self): - ex = 'ex' - ex2 = 'ex2' + ex = "ex" + ex2 = "ex2" handler_called = [False] scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, ex)] @@ -174,15 +201,20 @@ def create(): def handler(e, source): handler_called[0] = True raise Exception(ex2) + return o1.pipe(ops.catch(handler)) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_error(230, ex2)] - assert(handler_called[0]) + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_error(230, ex2), + ] + assert handler_called[0] def test_catch_nested_outer_catches(self): - ex = 'ex' + ex = "ex" first_handler_called = [False] second_handler_called = [False] scheduler = TestScheduler() @@ -197,9 +229,11 @@ def create(): def handler1(e, source): first_handler_called[0] = True return o2 + def handler2(e, source): second_handler_called[0] = True return o3 + return o1.pipe(ops.catch(handler1), ops.catch(handler2)) results = scheduler.start(create) @@ -209,8 +243,8 @@ def handler2(e, source): assert not second_handler_called[0] def test_catch_throw_from_nested_catch(self): - ex = 'ex' - ex2 = 'ex' + ex = "ex" + ex2 = "ex" first_handler_called = [False] second_handler_called = [False] scheduler = TestScheduler() @@ -224,18 +258,23 @@ def test_catch_throw_from_nested_catch(self): def create(): def handler1(e, source): first_handler_called[0] = True - assert(e == ex) + assert str(e) == ex return o2 + def handler2(e, source): second_handler_called[0] = True - assert(e == ex2) + assert str(e) == ex2 return o3 - return o1.pipe( - ops.catch(handler1), - ops.catch(handler2)) + + return o1.pipe(ops.catch(handler1), ops.catch(handler2)) results = scheduler.start(create) - assert results.messages == [on_next(210, 2), on_next(220, 3), on_next(230, 4), on_completed(235)] - assert(first_handler_called[0]) - assert(second_handler_called[0]) + assert results.messages == [ + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_completed(235), + ] + assert first_handler_called[0] + assert second_handler_called[0] diff --git a/tests/test_observable/test_distinctuntilchanged.py b/tests/test_observable/test_distinctuntilchanged.py index e7a9da15f..bcba55cdc 100644 --- a/tests/test_observable/test_distinctuntilchanged.py +++ b/tests/test_observable/test_distinctuntilchanged.py @@ -28,6 +28,7 @@ def test_distinct_until_changed_never(self): def create(): return rx.never().pipe(ops.distinct_until_changed()) + results = scheduler.start(create) assert results.messages == [] @@ -41,21 +42,28 @@ def create(): results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'C' and results[0].time == 250) + assert results[0].value.kind == "C" and results[0].time == 250 def test_distinct_until_changed_return(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(220, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(220, 2), on_completed(250) + ) def create(): return xs.pipe(ops.distinct_until_changed()) + results = scheduler.start(create).messages self.assertEqual(2, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 220 and results[0].value.value == 2) - assert(results[1].value.kind == 'C' and results[1].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 220 + and results[0].value.value == 2 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_distinct_until_changed_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) @@ -64,7 +72,11 @@ def create(): results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'E' and results[0].time == 250 and results[0].value.exception == ex) + assert ( + results[0].value.kind == "E" + and results[0].time == 250 + and str(results[0].value.exception) == ex + ) def test_distinct_until_changed_all_changes(self): scheduler = TestScheduler() @@ -74,110 +86,213 @@ def test_distinct_until_changed_all_changes(self): on_next(220, 3), on_next(230, 4), on_next(240, 5), - on_completed(250)) + on_completed(250), + ) def create(): return xs.pipe(ops.distinct_until_changed()) results = scheduler.start(create).messages self.assertEqual(5, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'N' and results[1].time == 220 and results[1].value.value == 3) - assert(results[2].value.kind == 'N' and results[2].time == 230 and results[2].value.value == 4) - assert(results[3].value.kind == 'N' and results[3].time == 240 and results[3].value.value == 5) - assert(results[4].value.kind == 'C' and results[4].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert ( + results[1].value.kind == "N" + and results[1].time == 220 + and results[1].value.value == 3 + ) + assert ( + results[2].value.kind == "N" + and results[2].time == 230 + and results[2].value.value == 4 + ) + assert ( + results[3].value.kind == "N" + and results[3].time == 240 + and results[3].value.value == 5 + ) + assert results[4].value.kind == "C" and results[4].time == 250 def test_distinct_until_changed_all_same(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 2), on_next(230, 2), on_next(240, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 2), + on_next(230, 2), + on_next(240, 2), + on_completed(250), + ) def create(): return xs.pipe(ops.distinct_until_changed()) results = scheduler.start(create).messages self.assertEqual(2, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'C' and results[1].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_distinct_until_changed_some_changes(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next(215, 3), on_next( - 220, 3), on_next(225, 2), on_next(230, 2), on_next(230, 1), on_next(240, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(215, 3), + on_next(220, 3), + on_next(225, 2), + on_next(230, 2), + on_next(230, 1), + on_next(240, 2), + on_completed(250), + ) def create(): return xs.pipe(ops.distinct_until_changed()) results = scheduler.start(create).messages self.assertEqual(6, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'N' and results[1].time == 215 and results[1].value.value == 3) - assert(results[2].value.kind == 'N' and results[2].time == 225 and results[2].value.value == 2) - assert(results[3].value.kind == 'N' and results[3].time == 230 and results[3].value.value == 1) - assert(results[4].value.kind == 'N' and results[4].time == 240 and results[4].value.value == 2) - assert(results[5].value.kind == 'C' and results[5].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert ( + results[1].value.kind == "N" + and results[1].time == 215 + and results[1].value.value == 3 + ) + assert ( + results[2].value.kind == "N" + and results[2].time == 225 + and results[2].value.value == 2 + ) + assert ( + results[3].value.kind == "N" + and results[3].time == 230 + and results[3].value.value == 1 + ) + assert ( + results[4].value.kind == "N" + and results[4].time == 240 + and results[4].value.value == 2 + ) + assert results[5].value.kind == "C" and results[5].time == 250 def test_distinct_until_changed_comparer_all_equal(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 3), on_next(230, 4), on_next(240, 5), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 3), + on_next(230, 4), + on_next(240, 5), + on_completed(250), + ) def create(): - return xs.pipe( - ops.distinct_until_changed(comparer=lambda x, y: True) - ) + return xs.pipe(ops.distinct_until_changed(comparer=lambda x, y: True)) results = scheduler.start(create).messages self.assertEqual(2, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'C' and results[1].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_distinct_until_changed_comparer_all_different(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 2), on_next(230, 2), on_next(240, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 2), + on_next(230, 2), + on_next(240, 2), + on_completed(250), + ) def create(): - return xs.pipe( - ops.distinct_until_changed(comparer=lambda x, y: False) - ) + return xs.pipe(ops.distinct_until_changed(comparer=lambda x, y: False)) results = scheduler.start(create).messages self.assertEqual(5, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'N' and results[1].time == 220 and results[1].value.value == 2) - assert(results[2].value.kind == 'N' and results[2].time == 230 and results[2].value.value == 2) - assert(results[3].value.kind == 'N' and results[3].time == 240 and results[3].value.value == 2) - assert(results[4].value.kind == 'C' and results[4].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert ( + results[1].value.kind == "N" + and results[1].time == 220 + and results[1].value.value == 2 + ) + assert ( + results[2].value.kind == "N" + and results[2].time == 230 + and results[2].value.value == 2 + ) + assert ( + results[3].value.kind == "N" + and results[3].time == 240 + and results[3].value.value == 2 + ) + assert results[4].value.kind == "C" and results[4].time == 250 def test_distinct_until_changed_key_mapper_div2(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next( - 220, 4), on_next(230, 3), on_next(240, 5), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), + on_next(210, 2), + on_next(220, 4), + on_next(230, 3), + on_next(240, 5), + on_completed(250), + ) def create(): return xs.pipe(ops.distinct_until_changed(lambda x: x % 2)) results = scheduler.start(create).messages self.assertEqual(3, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'N' and results[1].time == 230 and results[1].value.value == 3) - assert(results[2].value.kind == 'C' and results[2].time == 250) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert ( + results[1].value.kind == "N" + and results[1].time == 230 + and results[1].value.value == 3 + ) + assert results[2].value.kind == "C" and results[2].time == 250 def test_distinct_until_changed_key_mapper_throws(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(210, 2), on_completed(250) + ) def create(): return xs.pipe(ops.distinct_until_changed(lambda x: _raise(ex))) + results = scheduler.start(create) assert results.messages == [on_error(210, ex)] def test_distinct_until_changed_comparer_throws(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_next(220, 3), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(210, 2), on_next(220, 3), on_completed(250) + ) def create(): return xs.pipe( diff --git a/tests/test_observable/test_finally.py b/tests/test_observable/test_finally.py index 33c284000..456c08169 100644 --- a/tests/test_observable/test_finally.py +++ b/tests/test_observable/test_finally.py @@ -20,6 +20,7 @@ def test_finally_only_called_once_empty(self): def action(): invasserte_count[0] += 1 return invasserte_count + some_observable = rx.empty().pipe(ops.finally_action(action)) d = some_observable.subscribe() @@ -36,16 +37,19 @@ def create(): def action(): invasserted[0] = True return invasserted[0] + return xs.pipe(ops.finally_action(action)) results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'C' and results[0].time == 250) - assert(invasserted[0]) + assert results[0].value.kind == "C" and results[0].time == 250 + assert invasserted[0] def test_finally_return(self): scheduler = TestScheduler() - xs = scheduler.create_hot_observable(on_next(150, 1), on_next(210, 2), on_completed(250)) + xs = scheduler.create_hot_observable( + on_next(150, 1), on_next(210, 2), on_completed(250) + ) invasserted = [False] def create(): @@ -57,12 +61,16 @@ def action(): results = scheduler.start(create).messages self.assertEqual(2, len(results)) - assert(results[0].value.kind == 'N' and results[0].time == 210 and results[0].value.value == 2) - assert(results[1].value.kind == 'C' and results[1].time == 250) - assert(invasserted[0]) + assert ( + results[0].value.kind == "N" + and results[0].time == 210 + and results[0].value.value == 2 + ) + assert results[1].value.kind == "C" and results[1].time == 250 + assert invasserted[0] def test_finally_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) invasserted = [False] @@ -76,5 +84,9 @@ def action(): results = scheduler.start(create).messages self.assertEqual(1, len(results)) - assert(results[0].value.kind == 'E' and results[0].time == 250 and results[0].value.exception == ex) - assert(invasserted[0]) + assert ( + results[0].value.kind == "E" + and results[0].time == 250 + and str(results[0].value.exception) == ex + ) + assert invasserted[0] diff --git a/tests/test_observable/test_materialize.py b/tests/test_observable/test_materialize.py index cd98b8ed2..6f090f746 100644 --- a/tests/test_observable/test_materialize.py +++ b/tests/test_observable/test_materialize.py @@ -14,7 +14,6 @@ class TestMaterialize(unittest.TestCase): - def test_materialize_never(self): scheduler = TestScheduler() @@ -32,27 +31,40 @@ def create(): return xs.pipe(_.materialize()) results = scheduler.start(create).messages - assert(len(results) == 2) - assert(results[0].value.kind == 'N' and results[0].value.value.kind == 'C' and results[0].time == 250) - assert(results[1].value.kind == 'C' and results[1].time == 250) + assert len(results) == 2 + assert ( + results[0].value.kind == "N" + and results[0].value.value.kind == "C" + and results[0].time == 250 + ) + assert results[1].value.kind == "C" and results[1].time == 250 def test_materialize_return(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_completed(250)) + on_next(150, 1), on_next(210, 2), on_completed(250) + ) def create(): return xs.pipe(_.materialize()) results = scheduler.start(create).messages - assert(len(results) == 3) - assert(results[0].value.kind == 'N' and results[0].value.value.kind == - 'N' and results[0].value.value.value == 2 and results[0].time == 210) - assert(results[1].value.kind == 'N' and results[1].value.value.kind == 'C' and results[1].time == 250) - assert(results[2].value.kind == 'C' and results[1].time == 250) + assert len(results) == 3 + assert ( + results[0].value.kind == "N" + and results[0].value.value.kind == "N" + and results[0].value.value.value == 2 + and results[0].time == 210 + ) + assert ( + results[1].value.kind == "N" + and results[1].value.value.kind == "C" + and results[1].time == 250 + ) + assert results[2].value.kind == "C" and results[1].time == 250 def test_materialize_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) @@ -60,10 +72,13 @@ def create(): return xs.pipe(_.materialize()) results = scheduler.start(create).messages - assert(len(results) == 2) - assert(results[0].value.kind == 'N' and results[0].value.value.kind == - 'E' and results[0].value.value.exception == ex) - assert(results[1].value.kind == 'C') + assert len(results) == 2 + assert ( + results[0].value.kind == "N" + and results[0].value.value.kind == "E" + and str(results[0].value.value.exception) == ex + ) + assert results[1].value.kind == "C" def test_materialize_dematerialize_never(self): scheduler = TestScheduler() @@ -82,24 +97,29 @@ def create(): return xs.pipe(_.materialize(), _.dematerialize()) results = scheduler.start(create).messages - assert(len(results) == 1) - assert(results[0].value.kind == 'C' and results[0].time == 250) + assert len(results) == 1 + assert results[0].value.kind == "C" and results[0].time == 250 def test_materialize_dematerialize_return(self): scheduler = TestScheduler() xs = scheduler.create_hot_observable( - on_next(150, 1), on_next(210, 2), on_completed(250)) + on_next(150, 1), on_next(210, 2), on_completed(250) + ) def create(): return xs.pipe(_.materialize(), _.dematerialize()) results = scheduler.start(create).messages - assert(len(results) == 2) - assert(results[0].value.kind == 'N' and results[0].value.value == 2 and results[0].time == 210) - assert(results[1].value.kind == 'C') + assert len(results) == 2 + assert ( + results[0].value.kind == "N" + and results[0].value.value == 2 + and results[0].time == 210 + ) + assert results[1].value.kind == "C" def test_materialize_dematerialize_on_error(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() xs = scheduler.create_hot_observable(on_next(150, 1), on_error(250, ex)) @@ -107,9 +127,13 @@ def create(): return xs.pipe(_.materialize(), _.dematerialize()) results = scheduler.start(create).messages - assert(len(results) == 1) - assert(results[0].value.kind == 'E' and results[0].value.exception == ex and results[0].time == 250) + assert len(results) == 1 + assert ( + results[0].value.kind == "E" + and str(results[0].value.exception) == ex + and results[0].time == 250 + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_observable/test_onerrorresumenext.py b/tests/test_observable/test_onerrorresumenext.py index 0a4648695..7f7c60257 100644 --- a/tests/test_observable/test_onerrorresumenext.py +++ b/tests/test_observable/test_onerrorresumenext.py @@ -16,15 +16,9 @@ class TestOnErrorResumeNext(unittest.TestCase): def test_on_error_resume_next_no_errors(self): scheduler = TestScheduler() - msgs1 = [ - on_next(150, 1), - on_next(210, 2), - on_next(220, 3), - on_completed(230)] + msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_completed(230)] - msgs2 = [ - on_next(240, 4), - on_completed(250)] + msgs2 = [on_next(240, 4), on_completed(250)] o1 = scheduler.create_hot_observable(msgs1) o2 = scheduler.create_hot_observable(msgs2) @@ -37,43 +31,35 @@ def create(): on_next(210, 2), on_next(220, 3), on_next(240, 4), - on_completed(250)] + on_completed(250), + ] def test_on_error_resume_next_error(self): scheduler = TestScheduler() - msgs1 = [ - on_next(150, 1), - on_next(210, 2), - on_next(220, 3), - on_error(230, 'ex')] + msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, "ex")] - msgs2 = [ - on_next(240, 4), - on_completed(250)] + msgs2 = [on_next(240, 4), on_completed(250)] o1 = scheduler.create_hot_observable(msgs1) o2 = scheduler.create_hot_observable(msgs2) def create(): return o1.pipe(ops.on_error_resume_next(o2)) + results = scheduler.start(create) assert results.messages == [ on_next(210, 2), on_next(220, 3), on_next(240, 4), - on_completed(250)] + on_completed(250), + ] def test_on_error_resume_next_error_multiple(self): scheduler = TestScheduler() - msgs1 = [ - on_next(150, 1), - on_next(210, 2), - on_error(220, 'ex')] + msgs1 = [on_next(150, 1), on_next(210, 2), on_error(220, "ex")] - msgs2 = [ - on_next(230, 4), - on_error(240, 'ex')] + msgs2 = [on_next(230, 4), on_error(240, "ex")] msgs3 = [on_completed(250)] @@ -83,17 +69,17 @@ def test_on_error_resume_next_error_multiple(self): def create(): return rx.on_error_resume_next(o1, o2, o3) + results = scheduler.start(create) - assert results.messages == [ - on_next(210, 2), on_next(230, 4), on_completed(250)] + assert results.messages == [on_next(210, 2), on_next(230, 4), on_completed(250)] def test_on_error_resume_next_empty_return_throw_and_more(self): scheduler = TestScheduler() msgs1 = [on_next(150, 1), on_completed(205)] msgs2 = [on_next(215, 2), on_completed(220)] msgs3 = [on_next(225, 3), on_next(230, 4), on_completed(235)] - msgs4 = [on_error(240, 'ex')] + msgs4 = [on_error(240, "ex")] msgs5 = [on_next(245, 5), on_completed(250)] o1 = scheduler.create_hot_observable(msgs1) @@ -104,6 +90,7 @@ def test_on_error_resume_next_empty_return_throw_and_more(self): def create(): return rx.on_error_resume_next(o1, o2, o3, o4, o5) + results = scheduler.start(create) assert results.messages == [ @@ -111,31 +98,33 @@ def create(): on_next(225, 3), on_next(230, 4), on_next(245, 5), - on_completed(250)] + on_completed(250), + ] def test_on_error_resume_next_empty_return_throw_and_more_ii(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() - msgs1 = [ - on_next(150, 1), on_next(210, 2), on_completed(220)] + msgs1 = [on_next(150, 1), on_next(210, 2), on_completed(220)] msgs2 = [on_error(230, ex)] o1 = scheduler.create_hot_observable(msgs1) o2 = scheduler.create_hot_observable(msgs2) def create(): return o1.pipe(ops.on_error_resume_next(o2)) + results = scheduler.start(create) assert results.messages == [on_next(210, 2), on_completed(230)] def test_on_error_resume_next_single_source_throws(self): - ex = 'ex' + ex = "ex" scheduler = TestScheduler() msgs1 = [on_error(230, ex)] o1 = scheduler.create_hot_observable(msgs1) def create(): return rx.on_error_resume_next(o1) + results = scheduler.start(create) assert results.messages == [on_completed(230)] @@ -148,6 +137,7 @@ def test_on_error_resume_next_end_with_never(self): def create(): return rx.on_error_resume_next(o1, o2) + results = scheduler.start(create) assert results.messages == [on_next(210, 2)] @@ -167,15 +157,11 @@ def create(): def test_on_error_resume_next_start_with_factory(self): scheduler = TestScheduler() - msgs1 = [ - on_next(150, 1), - on_next(210, 2), - on_next(220, 3), - on_error(230, 'ex')] + msgs1 = [on_next(150, 1), on_next(210, 2), on_next(220, 3), on_error(230, "ex")] o1 = scheduler.create_hot_observable(msgs1) - def factory(ex): - assert(ex == "ex") + def factory(ex: Exception): + assert str(ex) == "ex" msgs2 = [on_next(240, 4), on_completed(250)] o2 = scheduler.create_hot_observable(msgs2) return o2 @@ -189,4 +175,5 @@ def create(): on_next(210, 2), on_next(220, 3), on_next(240, 4), - on_completed(250)] + on_completed(250), + ] diff --git a/tests/test_observable/test_scan.py b/tests/test_observable/test_scan.py index e8413b4ec..ed23bdacb 100644 --- a/tests/test_observable/test_scan.py +++ b/tests/test_observable/test_scan.py @@ -70,7 +70,7 @@ def create() -> Observable[int]: assert len(results) == 1 assert ( results[0].value.kind == "E" - and results[0].value.exception == ex + and str(results[0].value.exception) == ex and results[0].time == 250 ) @@ -175,7 +175,7 @@ def func(acc: int, x: int) -> int: assert ( results[0].value.kind == "E" and results[0].time == 250 - and results[0].value.exception == ex + and str(results[0].value.exception) == ex ) def test_scan_noseed_somedata(self): From b1c90fc2fac4a883b261429b58165472ff36366a Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 18 Feb 2022 07:51:12 +0100 Subject: [PATCH 098/103] Sort imports --- rx/core/operators/skipwhile.py | 2 +- rx/testing/marbles.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rx/core/operators/skipwhile.py b/rx/core/operators/skipwhile.py index 8b90f44d1..a8c96a1d1 100644 --- a/rx/core/operators/skipwhile.py +++ b/rx/core/operators/skipwhile.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, TypeVar, Tuple +from typing import Callable, Optional, Tuple, TypeVar from rx import operators as ops from rx.core import Observable, abc, pipe, typing diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index 834acea44..44f87087b 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -7,7 +7,7 @@ import rx from rx.core import Observable, typing from rx.core.abc.scheduler import SchedulerBase -from rx.core.notification import Notification, OnNext, OnError +from rx.core.notification import Notification, OnError, OnNext from rx.core.observable.marbles import parse from rx.core.typing import Callable, RelativeTime from rx.scheduler import NewThreadScheduler From ef9d697c2468907b27957075f16a6a82e18d43d1 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 18 Feb 2022 18:36:25 +0100 Subject: [PATCH 099/103] Typing fixes - Add pyright config file --- pyrightconfig.json | 7 +++++ rx/core/abc/asyncobservable.py | 9 ------- rx/core/abc/asyncobserver.py | 27 ------------------- rx/core/observer/__init__.py | 2 ++ rx/core/observer/observeonobserver.py | 2 +- rx/core/operators/amb.py | 2 +- rx/core/operators/groupbyuntil.py | 2 +- rx/core/operators/windowwithtimeorcount.py | 2 +- rx/internal/__init__.py | 18 ++++++++++++- rx/scheduler/catchscheduler.py | 2 +- rx/scheduler/currentthreadscheduler.py | 12 ++++++--- .../eventloop/asynciothreadsafescheduler.py | 12 +-------- rx/scheduler/threadpoolscheduler.py | 4 +-- rx/scheduler/virtualtimescheduler.py | 4 +-- rx/testing/marbles.py | 23 ++++++++++++---- 15 files changed, 62 insertions(+), 66 deletions(-) create mode 100644 pyrightconfig.json delete mode 100644 rx/core/abc/asyncobservable.py delete mode 100644 rx/core/abc/asyncobserver.py diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 000000000..b9a15da22 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,7 @@ +{ + "include": [ + "rx" + ], + "typeCheckingMode": "strict", + "reportImportCycles": false +} \ No newline at end of file diff --git a/rx/core/abc/asyncobservable.py b/rx/core/abc/asyncobservable.py deleted file mode 100644 index b426cff94..000000000 --- a/rx/core/abc/asyncobservable.py +++ /dev/null @@ -1,9 +0,0 @@ -from abc import ABCMeta, abstractmethod - - -class AsyncObservable(metaclass=ABCMeta): - __slots__ = () - - @abstractmethod - async def subscribe_async(self, observer): - raise NotImplementedError diff --git a/rx/core/abc/asyncobserver.py b/rx/core/abc/asyncobserver.py deleted file mode 100644 index a2bf79d05..000000000 --- a/rx/core/abc/asyncobserver.py +++ /dev/null @@ -1,27 +0,0 @@ -from abc import abstractmethod - -from .asyncobservable import AsyncObservable - - -class AsyncObserver(AsyncObservable): - """An asynchronous observable.""" - - __slots__ = () - - @abstractmethod - async def on_next_async(self, value): - return NotImplemented - - @abstractmethod - async def on_error_async(self, error): - return NotImplemented - - @abstractmethod - async def on_completed_async(self): - return NotImplemented - - async def subscribe_async(self, observer): - return self - - -__all__ = ["AsyncObserver"] diff --git a/rx/core/observer/__init__.py b/rx/core/observer/__init__.py index f6f084d36..d68f8991e 100644 --- a/rx/core/observer/__init__.py +++ b/rx/core/observer/__init__.py @@ -2,3 +2,5 @@ from .observeonobserver import ObserveOnObserver from .observer import Observer from .scheduledobserver import ScheduledObserver + +__all__ = ["AutoDetachObserver", "ObserveOnObserver", "Observer", "ScheduledObserver"] diff --git a/rx/core/observer/observeonobserver.py b/rx/core/observer/observeonobserver.py index 2cdfc3b87..8c4a20b96 100644 --- a/rx/core/observer/observeonobserver.py +++ b/rx/core/observer/observeonobserver.py @@ -1,4 +1,4 @@ -from typing import Any, TypeVar +from typing import TypeVar from .scheduledobserver import ScheduledObserver diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 3cc7ddc9d..31cfde169 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -1,5 +1,5 @@ from asyncio import Future -from typing import Callable, List, Optional, TypeVar, Union, cast +from typing import Callable, List, Optional, TypeVar, Union from rx import from_future from rx.core import Observable, abc diff --git a/rx/core/operators/groupbyuntil.py b/rx/core/operators/groupbyuntil.py index dec6f00f1..2474e3b9f 100644 --- a/rx/core/operators/groupbyuntil.py +++ b/rx/core/operators/groupbyuntil.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import Any, Callable, Optional, Type, TypeVar, cast +from typing import Any, Callable, Optional, TypeVar, cast from rx import operators as ops from rx.core import GroupedObservable, Observable, abc diff --git a/rx/core/operators/windowwithtimeorcount.py b/rx/core/operators/windowwithtimeorcount.py index 12ecf34dc..ee7dc6174 100644 --- a/rx/core/operators/windowwithtimeorcount.py +++ b/rx/core/operators/windowwithtimeorcount.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, List, Optional, TypeVar +from typing import Any, Callable, Optional, TypeVar from rx.core import Observable, abc, typing from rx.disposable import ( diff --git a/rx/internal/__init__.py b/rx/internal/__init__.py index a75aa8fb2..c90b5bdb1 100644 --- a/rx/internal/__init__.py +++ b/rx/internal/__init__.py @@ -1,4 +1,5 @@ -from . import concurrency, constants +from .concurrency import default_thread_factory, synchronized +from .constants import DELTA_ZERO, UTC_ZERO from .basic import default_comparer, default_error, noop from .exceptions import ( ArgumentOutOfRangeException, @@ -6,3 +7,18 @@ SequenceContainsNoElementsError, ) from .priorityqueue import PriorityQueue + +__all__ = [ + "ArgumentOutOfRangeException", + "DisposedException", + "default_comparer", + "default_error", + "noop", + "SequenceContainsNoElementsError", + "concurrency", + "DELTA_ZERO", + "UTC_ZERO", + "synchronized", + "default_thread_factory", + "PriorityQueue", +] diff --git a/rx/scheduler/catchscheduler.py b/rx/scheduler/catchscheduler.py index 92de32dda..145732b02 100644 --- a/rx/scheduler/catchscheduler.py +++ b/rx/scheduler/catchscheduler.py @@ -2,7 +2,7 @@ from typing import Callable, Optional, TypeVar, cast from rx.core import abc, typing -from rx.core.abc.scheduler import ScheduledAction, SchedulerBase +from rx.core.abc.scheduler import SchedulerBase from rx.disposable import Disposable, SingleAssignmentDisposable from .periodicscheduler import PeriodicScheduler diff --git a/rx/scheduler/currentthreadscheduler.py b/rx/scheduler/currentthreadscheduler.py index 94cdf2c50..7668d35b9 100644 --- a/rx/scheduler/currentthreadscheduler.py +++ b/rx/scheduler/currentthreadscheduler.py @@ -37,13 +37,17 @@ def singleton(cls) -> "CurrentThreadScheduler": thread = current_thread() class_map = CurrentThreadScheduler._global.get(cls) if class_map is None: - class_map = WeakKeyDictionary() - CurrentThreadScheduler._global[cls] = class_map + class_map_: MutableMapping[ + Thread, "CurrentThreadScheduler" + ] = WeakKeyDictionary() + CurrentThreadScheduler._global[cls] = class_map_ + else: + class_map_ = class_map try: - self = class_map[thread] + self = class_map_[thread] except KeyError: self = CurrentThreadSchedulerSingleton() - class_map[thread] = self + class_map_[thread] = self return self # pylint: disable=super-init-not-called diff --git a/rx/scheduler/eventloop/asynciothreadsafescheduler.py b/rx/scheduler/eventloop/asynciothreadsafescheduler.py index 4dbb7374f..057c4153e 100644 --- a/rx/scheduler/eventloop/asynciothreadsafescheduler.py +++ b/rx/scheduler/eventloop/asynciothreadsafescheduler.py @@ -1,7 +1,7 @@ import asyncio import logging from concurrent.futures import Future -from typing import TYPE_CHECKING, List, Optional, TypeVar +from typing import List, Optional, TypeVar from rx.core import abc, typing from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable @@ -18,16 +18,6 @@ class AsyncIOThreadSafeScheduler(AsyncIOScheduler): subclass of AsyncIOScheduler which uses the threadsafe asyncio methods. """ - def __init__(self, loop: asyncio.AbstractEventLoop) -> None: - """Create a new AsyncIOThreadSafeScheduler. - - Args: - loop: Instance of asyncio event loop to use; typically, you would - get this by asyncio.get_event_loop() - """ - - super().__init__(loop) - def schedule( self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None ) -> abc.DisposableBase: diff --git a/rx/scheduler/threadpoolscheduler.py b/rx/scheduler/threadpoolscheduler.py index a9b2e4375..936ebbc5c 100644 --- a/rx/scheduler/threadpoolscheduler.py +++ b/rx/scheduler/threadpoolscheduler.py @@ -1,5 +1,5 @@ from concurrent.futures import Future, ThreadPoolExecutor -from typing import Optional +from typing import Optional, Any from rx.core import abc, typing @@ -17,7 +17,7 @@ def __init__( ): self.executor: ThreadPoolExecutor = executor self.target: typing.StartableTarget = target - self.future: Optional[Future] = None + self.future: Optional["Future[Any]"] = None def start(self) -> None: self.future = self.executor.submit(self.target) diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index 92b5d2369..7248feee3 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -2,7 +2,7 @@ import threading from abc import abstractmethod from datetime import datetime, timedelta -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Any from rx.core import abc, typing from rx.internal import ArgumentOutOfRangeException, PriorityQueue @@ -114,7 +114,7 @@ def schedule_absolute( self._queue.enqueue(si) return si.disposable - def start(self) -> None: + def start(self) -> Any: """Starts the virtual time scheduler.""" with self._lock: diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index 44f87087b..de4eb19e3 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -1,12 +1,9 @@ -from collections import namedtuple from contextlib import contextmanager -from datetime import timedelta -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union, NamedTuple from warnings import warn import rx from rx.core import Observable, typing -from rx.core.abc.scheduler import SchedulerBase from rx.core.notification import Notification, OnError, OnNext from rx.core.observable.marbles import parse from rx.core.typing import Callable, RelativeTime @@ -18,7 +15,23 @@ new_thread_scheduler = NewThreadScheduler() -MarblesContext = namedtuple("MarblesContext", "start, cold, hot, exp") + +class MarblesContext(NamedTuple): + start: Callable[ + [Union[Observable[Any], Callable[[], Observable[Any]]]], List[Recorded[Any]] + ] + cold: Callable[ + [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]], + Observable[Any], + ] + hot: Callable[ + [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]], + Observable[Any], + ] + exp: Callable[ + [str, Optional[Dict[Union[str, float], Any]], Optional[Exception]], + List[Recorded[Any]], + ] @contextmanager From 41271aef65e7f32f2e476da332fa14b9b8f86690 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 18 Feb 2022 18:46:50 +0100 Subject: [PATCH 100/103] Sort imports --- rx/internal/__init__.py | 2 +- rx/scheduler/threadpoolscheduler.py | 2 +- rx/scheduler/virtualtimescheduler.py | 2 +- rx/testing/marbles.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rx/internal/__init__.py b/rx/internal/__init__.py index c90b5bdb1..fa37f9761 100644 --- a/rx/internal/__init__.py +++ b/rx/internal/__init__.py @@ -1,6 +1,6 @@ +from .basic import default_comparer, default_error, noop from .concurrency import default_thread_factory, synchronized from .constants import DELTA_ZERO, UTC_ZERO -from .basic import default_comparer, default_error, noop from .exceptions import ( ArgumentOutOfRangeException, DisposedException, diff --git a/rx/scheduler/threadpoolscheduler.py b/rx/scheduler/threadpoolscheduler.py index 936ebbc5c..32d9b074b 100644 --- a/rx/scheduler/threadpoolscheduler.py +++ b/rx/scheduler/threadpoolscheduler.py @@ -1,5 +1,5 @@ from concurrent.futures import Future, ThreadPoolExecutor -from typing import Optional, Any +from typing import Any, Optional from rx.core import abc, typing diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index 7248feee3..096854383 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -2,7 +2,7 @@ import threading from abc import abstractmethod from datetime import datetime, timedelta -from typing import Optional, TypeVar, Any +from typing import Any, Optional, TypeVar from rx.core import abc, typing from rx.internal import ArgumentOutOfRangeException, PriorityQueue diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index de4eb19e3..b8cece67f 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -1,5 +1,5 @@ from contextlib import contextmanager -from typing import Any, Dict, List, Optional, Tuple, Union, NamedTuple +from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union from warnings import warn import rx From afe9548fe2eabdea315d1c2bf22353e84ad11cda Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Fri, 18 Feb 2022 18:46:59 +0100 Subject: [PATCH 101/103] Update pyright config --- pyrightconfig.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pyrightconfig.json b/pyrightconfig.json index b9a15da22..0a350ed66 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,7 +1,12 @@ { - "include": [ - "rx" - ], - "typeCheckingMode": "strict", - "reportImportCycles": false + "include": [ + "rx" + ], + "exclude": [ + "tests" + ], + "reportImportCycles": false, + "reportMissingImports": false, + "pythonVersion": "3.9", + "typeCheckingMode": "basic" } \ No newline at end of file From 95f58ea5409b3e337d106b072467304a9098f7ea Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sat, 19 Feb 2022 09:24:33 +0100 Subject: [PATCH 102/103] Pyright passes at strict settings --- pyrightconfig.json | 2 +- rx/core/operators/amb.py | 2 +- rx/core/operators/contains.py | 8 +-- rx/core/operators/delay.py | 2 +- rx/core/operators/distinct.py | 4 +- rx/core/operators/distinctuntilchanged.py | 2 +- rx/core/operators/dowhile.py | 10 +++- rx/core/operators/partition.py | 19 ++++--- rx/core/operators/timeoutwithmapper.py | 10 ++-- rx/core/operators/timestamp.py | 8 +-- rx/core/operators/todict.py | 3 +- rx/core/operators/whiledo.py | 4 +- rx/operators/__init__.py | 8 ++- rx/scheduler/historicalscheduler.py | 30 ------------ rx/scheduler/mainloop/wxscheduler.py | 10 ++-- rx/scheduler/periodicscheduler.py | 60 ----------------------- rx/scheduler/virtualtimescheduler.py | 7 ++- rx/testing/coldobservable.py | 2 +- rx/testing/hotobservable.py | 2 +- rx/testing/marbles.py | 3 +- rx/testing/mockdisposable.py | 2 +- rx/testing/mockobserver.py | 2 +- rx/testing/testscheduler.py | 23 ++++----- 23 files changed, 73 insertions(+), 150 deletions(-) diff --git a/pyrightconfig.json b/pyrightconfig.json index 0a350ed66..62f6709a0 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -8,5 +8,5 @@ "reportImportCycles": false, "reportMissingImports": false, "pythonVersion": "3.9", - "typeCheckingMode": "basic" + "typeCheckingMode": "strict" } \ No newline at end of file diff --git a/rx/core/operators/amb.py b/rx/core/operators/amb.py index 31cfde169..3121582af 100644 --- a/rx/core/operators/amb.py +++ b/rx/core/operators/amb.py @@ -15,7 +15,7 @@ def amb_( if isinstance(right_source, Future): obs: Observable[_T] = from_future(right_source) else: - obs: Observable[_T] = right_source + obs = right_source def amb(left_source: Observable[_T]) -> Observable[_T]: def subscribe( diff --git a/rx/core/operators/contains.py b/rx/core/operators/contains.py index 769f190af..119805b41 100644 --- a/rx/core/operators/contains.py +++ b/rx/core/operators/contains.py @@ -15,10 +15,10 @@ def contains_( def predicate(v: _T) -> bool: return comparer_(v, value) - filtering = ops.filter(predicate) - something = ops.some() - - return pipe(filtering, something) + return pipe( + ops.filter(predicate), + ops.some(), + ) __all__ = ["contains_"] diff --git a/rx/core/operators/delay.py b/rx/core/operators/delay.py index 0418dc41a..b1694ad48 100644 --- a/rx/core/operators/delay.py +++ b/rx/core/operators/delay.py @@ -85,7 +85,7 @@ def action(scheduler: abc.SchedulerBase, state: Any = None): break should_continue = False - recurse_duetime = 0 + recurse_duetime: typing.RelativeTime = 0 if queue: should_continue = True diff = queue[0].timestamp - scheduler.now diff --git a/rx/core/operators/distinct.py b/rx/core/operators/distinct.py index 60484514e..5f74aef1b 100644 --- a/rx/core/operators/distinct.py +++ b/rx/core/operators/distinct.py @@ -32,7 +32,7 @@ def distinct_( key_mapper: Optional[typing.Mapper[_T, _TKey]] = None, comparer: Optional[typing.Comparer[_TKey]] = None, ) -> Callable[[Observable[_T]], Observable[_T]]: - comparer = comparer or default_comparer + comparer_ = comparer or default_comparer def distinct(source: Observable[_T]) -> Observable[_T]: """Returns an observable sequence that contains only distinct @@ -57,7 +57,7 @@ def subscribe( observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: - hashset = HashSet(comparer) + hashset = HashSet(comparer_) def on_next(x: _T) -> None: key = cast(_TKey, x) diff --git a/rx/core/operators/distinctuntilchanged.py b/rx/core/operators/distinctuntilchanged.py index 45b5fc724..ff7c664a6 100644 --- a/rx/core/operators/distinctuntilchanged.py +++ b/rx/core/operators/distinctuntilchanged.py @@ -45,7 +45,7 @@ def subscribe( scheduler: Optional[abc.SchedulerBase] = None, ) -> abc.DisposableBase: has_current_key = False - current_key: Optional[_TKey] = None + current_key: _TKey = cast(_TKey, None) def on_next(value: _T) -> None: nonlocal has_current_key, current_key diff --git a/rx/core/operators/dowhile.py b/rx/core/operators/dowhile.py index d9171c80e..b4642d5f4 100644 --- a/rx/core/operators/dowhile.py +++ b/rx/core/operators/dowhile.py @@ -7,7 +7,7 @@ def do_while_( - condition: Callable[[_T], bool] + condition: Callable[[Observable[_T]], bool] ) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a do while loop. @@ -22,7 +22,13 @@ def do_while_( """ def do_while(source: Observable[_T]) -> Observable[_T]: - return source.pipe(ops.concat(source.pipe(ops.while_do(condition)))) + return source.pipe( + ops.concat( + source.pipe( + ops.while_do(condition), + ), + ) + ) return do_while diff --git a/rx/core/operators/partition.py b/rx/core/operators/partition.py index b10829fb4..87ff88f7d 100644 --- a/rx/core/operators/partition.py +++ b/rx/core/operators/partition.py @@ -31,13 +31,16 @@ def partition(source: Observable[_T]) -> List[Observable[_T]]: predicate returns False. """ + def not_predicate(x: _T) -> bool: + return not predicate(x) + published = source.pipe( ops.publish(), ops.ref_count(), ) return [ published.pipe(ops.filter(predicate)), - published.pipe(ops.filter(lambda x: not predicate(x))), + published.pipe(ops.filter(not_predicate)), ] return partition @@ -67,14 +70,16 @@ def partition_indexed(source: Observable[_T]) -> List[Observable[_T]]: predicate returns False. """ - published = source.pipe(ops.publish(), ops.ref_count()) + def not_predicate_indexed(x: _T, i: int) -> bool: + return not predicate_indexed(x, i) + + published = source.pipe( + ops.publish(), + ops.ref_count(), + ) return [ published.pipe(ops.filter_indexed(predicate_indexed)), - published.pipe( - ops.filter_indexed( - predicate_indexed=lambda x, i: not predicate_indexed(x, i) - ) - ), + published.pipe(ops.filter_indexed(not_predicate_indexed)), ] return partition_indexed diff --git a/rx/core/operators/timeoutwithmapper.py b/rx/core/operators/timeoutwithmapper.py index 170a0b2f3..c8bdf22bd 100644 --- a/rx/core/operators/timeoutwithmapper.py +++ b/rx/core/operators/timeoutwithmapper.py @@ -38,8 +38,8 @@ def timeout_with_mapper_( of a timeout. """ - first_timeout = first_timeout or rx.never() - other = other or rx.throw(Exception("Timeout")) + first_timeout_ = first_timeout or rx.never() + other_ = other or rx.throw(Exception("Timeout")) def timeout_with_mapper(source: Observable[_T]) -> Observable[_T]: def subscribe( @@ -66,7 +66,7 @@ def timer_wins(): def on_next(x: Any) -> None: if timer_wins(): - subscription.disposable = other.subscribe( + subscription.disposable = other_.subscribe( observer, scheduler=scheduler ) @@ -78,13 +78,13 @@ def on_error(e: Exception) -> None: def on_completed() -> None: if timer_wins(): - subscription.disposable = other.subscribe(observer) + subscription.disposable = other_.subscribe(observer) d.disposable = timeout.subscribe( on_next, on_error, on_completed, scheduler=scheduler ) - set_timer(first_timeout) + set_timer(first_timeout_) def observer_wins(): res = not switched diff --git a/rx/core/operators/timestamp.py b/rx/core/operators/timestamp.py index ecadfe177..4644a349c 100644 --- a/rx/core/operators/timestamp.py +++ b/rx/core/operators/timestamp.py @@ -36,11 +36,11 @@ def timestamp(source: Observable[Any]) -> Observable[Timestamp[_T]]: def factory(scheduler_: Optional[abc.SchedulerBase] = None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() - mapper = operators.map( - lambda value: Timestamp(value=value, timestamp=_scheduler.now) - ) - return source.pipe(mapper) + def mapper(value: _T) -> Timestamp[_T]: + return Timestamp(value=value, timestamp=_scheduler.now) + + return source.pipe(operators.map(mapper)) return defer(factory) diff --git a/rx/core/operators/todict.py b/rx/core/operators/todict.py index d90976ece..9a3217503 100644 --- a/rx/core/operators/todict.py +++ b/rx/core/operators/todict.py @@ -35,13 +35,14 @@ def on_next(x: _T) -> None: observer.on_error(ex) return - element = x if element_mapper: try: element = element_mapper(x) except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) return + else: + element = cast(_TValue, x) m[key] = cast(_TValue, element) diff --git a/rx/core/operators/whiledo.py b/rx/core/operators/whiledo.py index 3714a3cb5..553c54074 100644 --- a/rx/core/operators/whiledo.py +++ b/rx/core/operators/whiledo.py @@ -10,7 +10,9 @@ _T = TypeVar("_T") -def while_do_(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def while_do_( + condition: Predicate[Observable[_T]], +) -> Callable[[Observable[_T]], Observable[_T]]: def while_do(source: Union[Observable[_T], "Future[_T]"]) -> Observable[_T]: """Repeats source as long as condition holds emulating a while loop. diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index ebeecaa33..fdbaf9917 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -862,7 +862,9 @@ def do_action( return do_action_(on_next, on_error, on_completed) -def do_while(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def do_while( + condition: Predicate[Observable[_T]], +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a do while loop. @@ -3891,7 +3893,9 @@ def to_set() -> Callable[[Observable[_T]], Observable[Set[_T]]]: return to_set_() -def while_do(condition: Predicate[_T]) -> Callable[[Observable[_T]], Observable[_T]]: +def while_do( + condition: Predicate[Observable[_T]], +) -> Callable[[Observable[_T]], Observable[_T]]: """Repeats source as long as condition holds emulating a while loop. diff --git a/rx/scheduler/historicalscheduler.py b/rx/scheduler/historicalscheduler.py index 3dc5f5721..e90b21d6d 100644 --- a/rx/scheduler/historicalscheduler.py +++ b/rx/scheduler/historicalscheduler.py @@ -1,8 +1,6 @@ from datetime import datetime from typing import Optional -from rx.core import typing - from .scheduler import UTC_ZERO from .virtualtimescheduler import VirtualTimeScheduler @@ -20,31 +18,3 @@ def __init__(self, initial_clock: Optional[datetime] = None) -> None: """ super().__init__(initial_clock or UTC_ZERO) - - @property - def now(self) -> datetime: - """Represents a notion of time for this scheduler. Tasks being - scheduled on a scheduler will adhere to the time denoted by this - property. - - Returns: - The scheduler's current time, as a datetime instance. - """ - - return self.to_datetime(self._clock) - - @classmethod - def add( - cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime - ) -> typing.AbsoluteTime: - """Adds a relative time value to an absolute time value. - - Args: - absolute: Absolute virtual time value. - relative: Relative virtual time value to add. - - Returns: - The resulting absolute virtual time sum value. - """ - - return cls.to_datetime(absolute) + cls.to_timedelta(relative) diff --git a/rx/scheduler/mainloop/wxscheduler.py b/rx/scheduler/mainloop/wxscheduler.py index 428955a5f..e5d3e2606 100644 --- a/rx/scheduler/mainloop/wxscheduler.py +++ b/rx/scheduler/mainloop/wxscheduler.py @@ -27,8 +27,8 @@ def __init__(self, wx: Any) -> None: timer_class: Any = self._wx.Timer class Timer(timer_class): - def __init__(self, callback) -> None: - super().__init__() + def __init__(self, callback: typing.Action) -> None: + super().__init__() # type: ignore self.callback = callback def Notify(self): @@ -44,7 +44,7 @@ def cancel_all(self) -> None: accessing dead wx objects in actions that might be pending. """ for timer in self._timers: - timer.Stop() + timer.Stop() # type: ignore def _wxtimer_schedule( self, @@ -71,13 +71,13 @@ def interval() -> None: log.debug("timeout wx: %s", msecs) timer = self._timer_class(interval) - timer.Start( + timer.Start( # type: ignore msecs, self._wx.TIMER_CONTINUOUS if periodic else self._wx.TIMER_ONE_SHOT ) self._timers.add(timer) def dispose() -> None: - timer.Stop() + timer.Stop() # type: ignore self._timers.remove(timer) return CompositeDisposable(sad, Disposable(dispose)) diff --git a/rx/scheduler/periodicscheduler.py b/rx/scheduler/periodicscheduler.py index 6069a0eb4..a5146ab55 100644 --- a/rx/scheduler/periodicscheduler.py +++ b/rx/scheduler/periodicscheduler.py @@ -1,4 +1,3 @@ -from abc import abstractmethod from datetime import datetime from typing import Optional, TypeVar @@ -59,62 +58,3 @@ def periodic( disp.disposable = self.schedule_relative(period, periodic, state=state) return disp - - @abstractmethod - def schedule( - self, action: typing.ScheduledAction[_TState], state: Optional[_TState] = None - ) -> abc.DisposableBase: - """Schedules an action to be executed. - - Args: - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented - - @abstractmethod - def schedule_relative( - self, - duetime: typing.RelativeTime, - action: typing.ScheduledAction[_TState], - state: Optional[_TState] = None, - ) -> abc.DisposableBase: - """Schedules an action to be executed after duetime. - - Args: - duetime: Relative time after which to execute the action. - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented - - @abstractmethod - def schedule_absolute( - self, - duetime: typing.AbsoluteTime, - action: typing.ScheduledAction[_TState], - state: Optional[_TState] = None, - ) -> abc.DisposableBase: - """Schedules an action to be executed at duetime. - - Args: - duetime: Absolute time at which to execute the action. - action: Action to be executed. - state: [Optional] state to be given to the action function. - - Returns: - The disposable object used to cancel the scheduled action - (best effort). - """ - - return NotImplemented diff --git a/rx/scheduler/virtualtimescheduler.py b/rx/scheduler/virtualtimescheduler.py index 096854383..0f7bd8b16 100644 --- a/rx/scheduler/virtualtimescheduler.py +++ b/rx/scheduler/virtualtimescheduler.py @@ -1,6 +1,5 @@ import logging import threading -from abc import abstractmethod from datetime import datetime, timedelta from typing import Any, Optional, TypeVar @@ -87,7 +86,7 @@ def schedule_relative( (best effort). """ - time: typing.AbsoluteTime = self.add(self._clock, self.to_seconds(duetime)) + time: typing.AbsoluteTime = self.add(self._clock, duetime) return self.schedule_absolute(time, action, state=state) def schedule_absolute( @@ -235,7 +234,6 @@ def sleep(self, time: typing.RelativeTime) -> None: self._clock = self.to_seconds(dt) @classmethod - @abstractmethod def add( cls, absolute: typing.AbsoluteTime, relative: typing.RelativeTime ) -> typing.AbsoluteTime: @@ -248,4 +246,5 @@ def add( Returns: The resulting absolute virtual time sum value. """ - raise NotImplementedError + + return cls.to_datetime(absolute) + cls.to_timedelta(relative) diff --git a/rx/testing/coldobservable.py b/rx/testing/coldobservable.py index 067e84420..e0326d6f3 100644 --- a/rx/testing/coldobservable.py +++ b/rx/testing/coldobservable.py @@ -16,7 +16,7 @@ def __init__( ) -> None: super().__init__() - self.scheduler: VirtualTimeScheduler = scheduler + self.scheduler = scheduler self.messages = messages self.subscriptions: List[Subscription] = [] diff --git a/rx/testing/hotobservable.py b/rx/testing/hotobservable.py index a4696f1db..43b7c0562 100644 --- a/rx/testing/hotobservable.py +++ b/rx/testing/hotobservable.py @@ -17,7 +17,7 @@ def __init__( ) -> None: super().__init__() - self.scheduler: VirtualTimeScheduler = scheduler + self.scheduler = scheduler self.messages = messages self.subscriptions: List[Subscription] = [] self.observers: List[abc.ObserverBase[_T]] = [] diff --git a/rx/testing/marbles.py b/rx/testing/marbles.py index b8cece67f..2ddb21a2d 100644 --- a/rx/testing/marbles.py +++ b/rx/testing/marbles.py @@ -101,9 +101,10 @@ def test_start( check() if isinstance(create, Observable): + create_ = create def default_create() -> Observable[Any]: - return create + return create_ create_function = default_create else: diff --git a/rx/testing/mockdisposable.py b/rx/testing/mockdisposable.py index fce202676..092bf531c 100644 --- a/rx/testing/mockdisposable.py +++ b/rx/testing/mockdisposable.py @@ -6,7 +6,7 @@ class MockDisposable(abc.DisposableBase): def __init__(self, scheduler: VirtualTimeScheduler): - self.scheduler: VirtualTimeScheduler = scheduler + self.scheduler = scheduler self.disposes: List[typing.AbsoluteTime] = [] self.disposes.append(self.scheduler.clock) diff --git a/rx/testing/mockobserver.py b/rx/testing/mockobserver.py index fe8109d23..b5f7ab33b 100644 --- a/rx/testing/mockobserver.py +++ b/rx/testing/mockobserver.py @@ -11,7 +11,7 @@ class MockObserver(abc.ObserverBase[_T]): def __init__(self, scheduler: VirtualTimeScheduler) -> None: - self.scheduler: VirtualTimeScheduler = scheduler + self.scheduler = scheduler self.messages: List[Recorded[_T]] = [] def on_next(self, value: _T) -> None: diff --git a/rx/testing/testscheduler.py b/rx/testing/testscheduler.py index 03d005e2b..76fe03a44 100644 --- a/rx/testing/testscheduler.py +++ b/rx/testing/testscheduler.py @@ -45,12 +45,6 @@ def schedule_absolute( duetime = duetime if isinstance(duetime, float) else self.to_seconds(duetime) return super().schedule_absolute(duetime, action, state) - @classmethod - def add(cls, absolute, relative): - """Adds a relative virtual time to an absolute virtual time value""" - - return absolute + relative - def start( self, create: Optional[Callable[[], Observable[_T]]] = None, @@ -78,34 +72,35 @@ def start( """ # Defaults - create = create or rx.never created = created or ReactiveTest.created subscribed = subscribed or ReactiveTest.subscribed disposed = disposed or ReactiveTest.disposed observer = self.create_observer() - subscription: List[Optional[abc.DisposableBase]] = [None] - source: List[Optional[abc.ObservableBase[_T]]] = [None] + subscription: Optional[abc.DisposableBase] = None + source: Optional[abc.ObservableBase[_T]] = None def action_create(scheduler: abc.SchedulerBase, state: Any = None): """Called at create time. Defaults to 100""" - source[0] = create() + nonlocal source + source = create() if create is not None else rx.never() return Disposable() self.schedule_absolute(created, action_create) def action_subscribe(scheduler: abc.SchedulerBase, state: Any = None): """Called at subscribe time. Defaults to 200""" - assert source[0] - subscription[0] = source[0].subscribe(observer, scheduler=scheduler) + nonlocal subscription + if source: + subscription = source.subscribe(observer, scheduler=scheduler) return Disposable() self.schedule_absolute(subscribed, action_subscribe) def action_dispose(scheduler: abc.SchedulerBase, state: Any = None): """Called at dispose time. Defaults to 1000""" - assert subscription[0] - subscription[0].dispose() + if subscription: + subscription.dispose() return Disposable() self.schedule_absolute(disposed, action_dispose) From 2716a45d2ce109a7dddbeb7ca53d633de883fb41 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 20 Feb 2022 09:08:32 +0100 Subject: [PATCH 103/103] Use naming convention for operator implementations --- rx/core/operators/map.py | 7 +++---- rx/operators/__init__.py | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/rx/core/operators/map.py b/rx/core/operators/map.py index 729ffd6e5..94d26c597 100644 --- a/rx/core/operators/map.py +++ b/rx/core/operators/map.py @@ -10,8 +10,7 @@ _T2 = TypeVar("_T2") -# pylint: disable=redefined-builtin -def map( +def map_( mapper: Optional[Mapper[_T1, _T2]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: @@ -55,7 +54,7 @@ def on_next(value: _T1) -> None: return map -def map_indexed( +def map_indexed_( mapper_indexed: Optional[MapperIndexed[_T1, _T2]] = None ) -> Callable[[Observable[_T1]], Observable[_T2]]: def _identity(value: _T1, _: int) -> _T2: @@ -69,4 +68,4 @@ def _identity(value: _T1, _: int) -> _T2: ) -__all__ = ["map", "map_indexed"] +__all__ = ["map_", "map_indexed_"] diff --git a/rx/operators/__init__.py b/rx/operators/__init__.py index fdbaf9917..8f8deb0cf 100644 --- a/rx/operators/__init__.py +++ b/rx/operators/__init__.py @@ -1777,9 +1777,9 @@ def map( the result of invoking the transform function on each element of the source. """ - from rx.core.operators.map import map + from rx.core.operators.map import map_ - return map(mapper) + return map_(mapper) def map_indexed( @@ -1809,9 +1809,9 @@ def map_indexed( the result of invoking the transform function on each element of the source. """ - from rx.core.operators.map import map_indexed + from rx.core.operators.map import map_indexed_ - return map_indexed(mapper_indexed) + return map_indexed_(mapper_indexed) def materialize() -> Callable[[Observable[_T]], Observable[Notification[_T]]]: