WIP: #170 #172

Draft
edraft wants to merge 20 commits from #170 into master
8 changed files with 49 additions and 43 deletions
Showing only changes of commit 30b163a440 - Show all commits

View File

@ -8,7 +8,7 @@ from cpl_reactive_extensions.subscriber import Subscriber
class Interval(Observable): class Interval(Observable):
def __init__(self, interval: float, callback: Callable = None): def __init__(self, interval: float, callback: Callable = None, not_in_background=False):
self._interval = interval self._interval = interval
callback = callback if callback is not None else self._default_callback callback = callback if callback is not None else self._default_callback
@ -26,7 +26,7 @@ class Interval(Observable):
t = threading.Thread(target=schedule, args=(x,)) t = threading.Thread(target=schedule, args=(x,))
t.start() t.start()
Observable.__init__(self, thread) Observable.__init__(self, schedule if not_in_background else thread)
self._i = 0 self._i = 0
def _run(self, scheduler, x: Subscriber, callback: Callable): def _run(self, scheduler, x: Subscriber, callback: Callable):

View File

@ -17,7 +17,7 @@ class Observable(Subscribable):
self._subscribe = subscribe self._subscribe = subscribe
self._source: Optional[Observable] = None self._source: Optional[Observable] = None
self._operator: Optional[Operator] = None self._operator: Optional[Callable] = None
@staticmethod @staticmethod
def from_list(values: list): def from_list(values: list):
@ -38,7 +38,7 @@ class Observable(Subscribable):
observable = Observable(callback) observable = Observable(callback)
return observable return observable
def lift(self, operator: Operator) -> Observable: def lift(self, operator: Callable) -> Observable:
observable = Observable() observable = Observable()
observable._source = self observable._source = self
observable._operator = operator observable._operator = operator
@ -75,7 +75,7 @@ class Observable(Subscribable):
) )
subscriber.add( subscriber.add(
self._operator.call(subscriber, self._source) self._operator(subscriber, self._source)
if self._operator is not None if self._operator is not None
else self._subscribe(subscriber) else self._subscribe(subscriber)
if self._source is not None if self._source is not None
@ -84,18 +84,8 @@ class Observable(Subscribable):
return subscriber return subscriber
def _call(self, observer: Observer):
try:
self._subscribe(observer)
except Exception as e:
observer.error(e)
def pipe(self, *args) -> Observable: def pipe(self, *args) -> Observable:
# observables = [] return self._pipe_from_array(args)(self)
# for arg in args:
# observable = arg(self)
# observables.append(observable)
return self._pipe_from_array(args)
def _pipe_from_array(self, args): def _pipe_from_array(self, args):
if len(args) == 0: if len(args) == 0:

View File

@ -2,9 +2,10 @@ from typing import Callable
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions import Subscriber from cpl_reactive_extensions import Subscriber
from cpl_reactive_extensions.abc import Observer
class OperatorSubscriber(Subscriber): class OperatorSubscriber(Subscriber, Observer):
def __init__( def __init__(
self, self,
destination: Subscriber, destination: Subscriber,
@ -14,19 +15,19 @@ class OperatorSubscriber(Subscriber):
on_finalize: Callable = None, on_finalize: Callable = None,
should_unsubscribe: Callable = None, should_unsubscribe: Callable = None,
): ):
Subscriber.__init__(self) Subscriber.__init__(self, destination)
self._on_finalize = on_finalize self._on_finalize = on_finalize
self._should_unsubscribe = should_unsubscribe self._should_unsubscribe = should_unsubscribe
def on_next_wrapper(self: OperatorSubscriber, value: T): def on_next_wrapper(value: T):
try: try:
on_next(value) on_next(value)
except Exception as e: except Exception as e:
destination.error(e) destination.error(e)
self._on_next = on_next_wrapper if on_next is not None else super()._on_next self._on_next = on_next_wrapper if on_next is not None else self._on_next
def on_error_wrapper(self: OperatorSubscriber, value: T): def on_error_wrapper(value: T):
try: try:
on_error(value) on_error(value)
except Exception as e: except Exception as e:
@ -34,9 +35,9 @@ class OperatorSubscriber(Subscriber):
finally: finally:
self.unsubscribe() self.unsubscribe()
self._on_error = on_error_wrapper if on_error is not None else super()._on_error self._on_error = on_error_wrapper if on_error is not None else self._on_error
def on_complete_wrapper(self: OperatorSubscriber, value: T): def on_complete_wrapper(value: T):
try: try:
on_complete(value) on_complete(value)
except Exception as e: except Exception as e:
@ -44,10 +45,11 @@ class OperatorSubscriber(Subscriber):
finally: finally:
self.unsubscribe() self.unsubscribe()
self._on_complete = on_complete_wrapper if on_complete is not None else super()._on_complete self._on_complete = on_complete_wrapper if on_complete is not None else self._on_complete
def unsubscribe(self): def unsubscribe(self):
if self._should_unsubscribe and not self._should_unsubscribe(): if self._should_unsubscribe is not None and not self._should_unsubscribe():
return return
super().unsubscribe() Subscriber.unsubscribe(self)
not self.closed and self._on_finalize is not None and self._on_finalize() if not self.closed and self._on_finalize is not None:
self._on_finalize()

View File

@ -11,7 +11,7 @@ def take(count: int):
def init(source: Observable, subscriber: Subscriber): def init(source: Observable, subscriber: Subscriber):
seen = 0 seen = 0
def sub(value: T): def on_next(value: T):
nonlocal seen nonlocal seen
if seen + 1 <= count: if seen + 1 <= count:
@ -20,7 +20,9 @@ def take(count: int):
if count <= seen: if count <= seen:
subscriber.complete() subscriber.complete()
else:
sub.unsubscribe()
source.subscribe(OperatorSubscriber(subscriber, sub)) sub = source.subscribe(OperatorSubscriber(subscriber, on_next))
return operate(init) return operate(init)

View File

@ -21,29 +21,45 @@ class Subscriber(Subscription, Observer):
self._on_error = on_error self._on_error = on_error
self._on_complete = on_complete self._on_complete = on_complete
def _next(self, value: T):
self._on_next(value)
def next(self, value: T): def next(self, value: T):
if self.is_stopped: if self.is_stopped:
raise Exception("Observer is closed") raise Exception("Observer is closed")
self._on_next(value) self._next(value)
def _error(self, ex: Exception):
try:
self._on_error(ex)
finally:
self.unsubscribe()
def error(self, ex: Exception): def error(self, ex: Exception):
if self.is_stopped: if self.is_stopped:
return return
self._on_error(ex) self._error(ex)
def _complete(self):
try:
self._on_complete()
finally:
self.unsubscribe()
def complete(self): def complete(self):
if self.is_stopped: if self.is_stopped:
return return
self.is_stopped = True self.is_stopped = True
self._on_complete() self._complete()
def unsubscribe(self): def unsubscribe(self):
if self._closed: if self._closed:
return return
super().unsubscribe() self.is_stopped = True
Subscription.unsubscribe(self)
self._on_next = None self._on_next = None
self._on_error = None self._on_error = None
self._on_complete = None self._on_complete = None

View File

@ -62,14 +62,14 @@ class Subscription(Unsubscribable):
except Exception as e: except Exception as e:
print(e) print(e)
for finalizer in self._finalizers: finalizers = self._finalizers
self._finalizers = None
for finalizer in finalizers:
try: try:
self._exec_finalizer(finalizer) self._exec_finalizer(finalizer)
except Exception as e: except Exception as e:
print(e) print(e)
self._finalizers = None
def add(self, tear_down: Union[Subscription, Unsubscribable]): def add(self, tear_down: Union[Subscription, Unsubscribable]):
if tear_down is None or tear_down == self: if tear_down is None or tear_down == self:
return return

View File

@ -1,10 +1,9 @@
from typing import Callable from typing import Callable
from cpl_reactive_extensions import Observable, Subscriber from cpl_reactive_extensions import Observable, Subscriber
from cpl_reactive_extensions.abc import Operator
def operate(init: Callable[[Observable, Subscriber], Operator]): def operate(init: Callable[[Observable, Subscriber], None]):
def observable(source: Observable): def observable(source: Observable):
def create(self: Subscriber, lifted_source: Observable): def create(self: Subscriber, lifted_source: Observable):
try: try:
@ -12,12 +11,9 @@ def operate(init: Callable[[Observable, Subscriber], Operator]):
except Exception as e: except Exception as e:
self.error(e) self.error(e)
operator = Operator()
operator.call = create
if "lift" not in dir(source): if "lift" not in dir(source):
raise TypeError("Unable to lift unknown Observable type") raise TypeError("Unable to lift unknown Observable type")
return source.lift(operator) return source.lift(create)
return observable return observable

View File

@ -23,5 +23,5 @@ class ObservableOperatorTestCase(unittest.TestCase):
def sub(x): def sub(x):
Console.write_line(x) Console.write_line(x)
observable = Interval(1.0) observable = Interval(0.1)
sub = observable.pipe(take(2)).subscribe(sub) sub = observable.pipe(take(2)).subscribe(sub)