WIP: #170 #172

Draft
edraft wants to merge 20 commits from #170 into master
51 changed files with 572 additions and 71 deletions
Showing only changes of commit aabbfeaa92 - Show all commits

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -23,4 +23,4 @@ from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.application"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -29,4 +29,4 @@ from .startup_abc import StartupABC
from .startup_extension_abc import StartupExtensionABC from .startup_extension_abc import StartupExtensionABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.configuration"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -35,4 +35,4 @@ from .validator_abc import ValidatorABC
from .variable_argument import VariableArgument from .variable_argument import VariableArgument
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.console"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -28,4 +28,4 @@ from .foreground_color_enum import ForegroundColorEnum
from .spinner_thread import SpinnerThread from .spinner_thread import SpinnerThread
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -4,7 +4,7 @@
"Version": { "Version": {
"Major": "2023", "Major": "2023",
"Minor": "4", "Minor": "4",
"Micro": "0.post1" "Micro": "dev170"
}, },
"Author": "Sven Heidemann", "Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de", "AuthorEmail": "sven.heidemann@sh-edraft.de",

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.database"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -26,4 +26,4 @@ from .database_settings import DatabaseSettings
from .table_abc import TableABC from .table_abc import TableABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.database.connection"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -25,4 +25,4 @@ from .database_connection import DatabaseConnection
from .database_connection_abc import DatabaseConnectionABC from .database_connection_abc import DatabaseConnectionABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.database.context"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -25,4 +25,4 @@ from .database_context import DatabaseContext
from .database_context_abc import DatabaseContextABC from .database_context_abc import DatabaseContextABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.dependency_injection"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -31,4 +31,4 @@ from .service_provider import ServiceProvider
from .service_provider_abc import ServiceProviderABC from .service_provider_abc import ServiceProviderABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.environment"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -26,4 +26,4 @@ from .environment_name_enum import EnvironmentNameEnum
from .application_environment import ApplicationEnvironment from .application_environment import ApplicationEnvironment
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.logging"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -28,4 +28,4 @@ from .logging_settings import LoggingSettings
from .logging_settings_name_enum import LoggingSettingsNameEnum from .logging_settings_name_enum import LoggingSettingsNameEnum
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.mailing"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -28,4 +28,4 @@ from .email_client_settings import EMailClientSettings
from .email_client_settings_name_enum import EMailClientSettingsNameEnum from .email_client_settings_name_enum import EMailClientSettingsNameEnum
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.pipes"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -26,4 +26,4 @@ from .ip_address_pipe import IPAddressPipe
from .pipe_abc import PipeABC from .pipe_abc import PipeABC
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.time"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -25,4 +25,4 @@ from .time_format_settings import TimeFormatSettings
from .time_format_settings_names_enum import TimeFormatSettingsNamesEnum from .time_format_settings_names_enum import TimeFormatSettingsNamesEnum
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -1,3 +1,4 @@
from typing import TypeVar from typing import TypeVar, Union
T = TypeVar("T") T = TypeVar("T")
Number = Union[int, float]

View File

@ -15,7 +15,7 @@ __title__ = "cpl_core.utils"
__author__ = "Sven Heidemann" __author__ = "Sven Heidemann"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de" __copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.4.0.post1" __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
@ -26,4 +26,4 @@ from .string import String
from .pip import Pip from .pip import Pip
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="0.post1") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -19,14 +19,8 @@ __version__ = "2023.4.dev170"
from collections import namedtuple from collections import namedtuple
# imports # imports
from .behavior_subject import BehaviorSubject
from .interval import Interval
from .observable import Observable
from .subject import Subject
from .subscriber import Subscriber
from .subscription import Subscription
from .type import ObserverOrCallable
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -21,11 +21,6 @@ from collections import namedtuple
# imports # imports
from .observer import Observer
from .operator import Operator
from .subscribable import Subscribable
from .unsubscribable import Unsubscribable
VersionInfo = namedtuple("VersionInfo", "major minor micro") VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170") version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -1,6 +1,6 @@
from typing import Any from typing import Any
from cpl_reactive_extensions.subscriber import Subscriber from cpl_reactive_extensions.internal.subscriber import Subscriber
class Operator: class Operator:

View File

@ -0,0 +1,10 @@
from abc import ABC, abstractmethod
from cpl_core.type import T, Number
from cpl_reactive_extensions.internal.subscription import Subscription
class SchedulerAction(ABC):
@abstractmethod
def schedule(self, state: T = None, delay: Number = None) -> Subscription:
pass

View File

@ -0,0 +1,12 @@
from abc import ABC, abstractmethod
from typing import Callable, Optional
from cpl_core.type import Number, T
from cpl_reactive_extensions.internal.subscription import Subscription
from cpl_reactive_extensions.abc.scheduler_action import SchedulerAction
class SchedulerLike(ABC):
@abstractmethod
def schedule(self, work: Callable[[SchedulerAction, Optional[T]], None], delay: Number, state: T) -> Subscription:
pass

View File

@ -16,7 +16,7 @@
"LicenseName": "MIT", "LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.", "LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [ "Dependencies": [
"cpl-core>=2023.4.0" "cpl-core>=2023.4.dev170"
], ],
"DevDependencies": [ "DevDependencies": [
"cpl-cli>=2023.4.0" "cpl-cli>=2023.4.0"

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
cpl-reactive-extensions CPL Simple ReactiveX implementation
~~~~~~~~~~~~~~~~~~~
CPL Simple ReactiveX implementation, see RxJS and RxPy for detailed implementation.
:copyright: (c) 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_reactive_extensions.helper"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2023 sh-edraft.de"
__version__ = "2023.4.dev170"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -0,0 +1,11 @@
def bind(instance, func, *args, as_name=None):
"""
Bind the function *func* to *instance*, with either provided name *as_name*
or the existing name of *func*. The provided *func* should accept the
instance as the first argument, i.e. "self".
"""
if as_name is None:
as_name = func.__name__
bound_method = func.__get__(instance, instance.__class__)
setattr(instance, as_name, bound_method)
return bound_method

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
cpl-reactive-extensions CPL Simple ReactiveX implementation
~~~~~~~~~~~~~~~~~~~
CPL Simple ReactiveX implementation, see RxJS and RxPy for detailed implementation.
:copyright: (c) 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_reactive_extensions.internal"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2023 sh-edraft.de"
__version__ = "2023.4.dev170"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -0,0 +1,10 @@
from cpl_core.type import T, Number
from cpl_reactive_extensions.internal.subscription import Subscription
class Action(Subscription):
def __init__(self, scheduler, work):
Subscription.__init__(self)
def schedule(self, state: T = None, delay: Number = 0) -> Subscription:
return self

View File

@ -0,0 +1,97 @@
from typing import Optional
from cpl_core.type import T, Number
from cpl_reactive_extensions.internal.action import Action
from cpl_reactive_extensions.internal.subscription import Subscription
from cpl_reactive_extensions.timer import Timer
class AsyncAction(Action):
def __init__(self, scheduler, work):
from cpl_reactive_extensions.scheduler.async_scheduler import AsyncScheduler
Action.__init__(self, scheduler, work)
self._scheduler: AsyncScheduler = scheduler
self._work = work
self.timer = None
self.state: Optional[T] = None
self.delay: Number = 0
self._pending = False
def schedule(self, state: T = None, delay: Number = 0) -> Subscription:
if self.closed:
return self
self.state = state
timer = self.timer
scheduler = self._scheduler
if timer is not None:
self.timer = self.recycle_async_timer(scheduler, timer, delay)
self._pending = True
self.delay = delay
self.timer = self.timer if self.timer is not None else self.request_async_timer(scheduler, delay)
return self
def request_async_timer(self, scheduler, delay: Number = 0):
from cpl_reactive_extensions.scheduler.async_scheduler import AsyncScheduler
scheduler: AsyncScheduler = scheduler
return Timer(delay, lambda: scheduler.flush(self))
def recycle_async_timer(self, scheduler, timer=None, delay: Number = None):
from cpl_reactive_extensions.scheduler.async_scheduler import AsyncScheduler
scheduler: AsyncScheduler = scheduler
if delay is None and self.delay == delay and not self._pending:
return timer
if timer is not None:
timer.clear()
return None
def execute(self, state: T, delay: Number):
if self.closed:
return Exception("Executing cancelled action")
self._pending = False
error = self._execute(state, delay)
if error is not None:
return error
elif not self._pending and self.timer is not None:
self._timer = self.recycle_async_timer(self._scheduler, self.timer, None)
def _execute(self, state: T, delay: Number):
errored = False
ex = None
try:
self._work(state)
except Exception as e:
errored = True
ex = e
if errored:
self.unsubscribe()
return ex
def unsubscribe(self):
if self.closed:
return
self._scheduler.actions.remove(self)
if self._timer is not None:
self._timer = self.recycle_async_timer(self._scheduler, self.timer, None)
self._work = None
self.state = None
self._scheduler = None
self._pending = False
self.delay = None
Action.unsubscribe(self)

View File

@ -1,8 +1,8 @@
from typing import Callable 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.abc import Observer from cpl_reactive_extensions.abc import Observer
from cpl_reactive_extensions.internal.subscriber import Subscriber
class OperatorSubscriber(Subscriber, Observer): class OperatorSubscriber(Subscriber, Observer):

View File

@ -2,7 +2,7 @@ from typing import Callable
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions.abc.observer import Observer from cpl_reactive_extensions.abc.observer import Observer
from cpl_reactive_extensions.subscription import Subscription from cpl_reactive_extensions.internal.subscription import Subscription
from cpl_reactive_extensions.type import ObserverOrCallable from cpl_reactive_extensions.type import ObserverOrCallable

View File

@ -0,0 +1,10 @@
from typing import Callable
from cpl_core.type import Number
from cpl_reactive_extensions.timer import Timer
class TimerProvider:
@staticmethod
def set_timer(handler: Callable, timeout: Number = None, *args):
return Timer(timeout, handler, *args)

View File

@ -1,6 +1,7 @@
from typing import Callable from typing import Callable
from cpl_reactive_extensions import Observable, Subscriber from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.internal.subscriber import Subscriber
def operate(init: Callable[[Observable, Subscriber], None]): def operate(init: Callable[[Observable, Subscriber], None]):

View File

@ -3,8 +3,8 @@ import threading
import time import time
from typing import Callable from typing import Callable
from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.observable import Observable from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.subscriber import Subscriber
class Interval(Observable): class Interval(Observable):

View File

@ -1,11 +1,12 @@
from __future__ import annotations from __future__ import annotations
from typing import Callable, Any, Optional, Type from typing import Callable, Any, Optional
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions.abc.observer import Observer
from cpl_reactive_extensions.abc.subscribable import Subscribable from cpl_reactive_extensions.abc.subscribable import Subscribable
from cpl_reactive_extensions.subscriber import Observer, Subscriber from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.subscription import Subscription from cpl_reactive_extensions.internal.subscription import Subscription
from cpl_reactive_extensions.type import ObserverOrCallable from cpl_reactive_extensions.type import ObserverOrCallable

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
cpl-reactive-extensions CPL Simple ReactiveX implementation
~~~~~~~~~~~~~~~~~~~
CPL Simple ReactiveX implementation, see RxJS and RxPy for detailed implementation.
:copyright: (c) 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_reactive_extensions.operators"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2023 sh-edraft.de"
__version__ = "2023.4.dev170"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -0,0 +1,62 @@
from typing import Optional
from cpl_core.type import T, Number
from cpl_reactive_extensions.abc import SchedulerAction
from cpl_reactive_extensions.internal.operator_subscriber import OperatorSubscriber
from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.internal.subscription import Subscription
from cpl_reactive_extensions.internal.utils import operate
from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.scheduler.async_scheduler import async_scheduler
def debounce_time(time: Number, scheduler=async_scheduler):
def init(source: Observable, subscriber: Subscriber):
active_task: Optional[Subscription] = None
last_value: Optional[T] = None
last_time: Optional[Number] = None
def emit():
nonlocal active_task, last_value
if active_task is None:
return
active_task.unsubscribe()
active_task = None
value = last_value
last_value = None
subscriber.next(value)
def emit_when_idle(action: SchedulerAction):
nonlocal active_task, last_time
target_time = last_time + time
now = scheduler.now
if now < target_time:
active_task = action.schedule(None, target_time - now)
subscriber.add(active_task)
return
emit()
def on_next(value: T):
nonlocal active_task, last_value
last_value = value
if active_task is None:
active_task = scheduler.schedule(emit_when_idle, time)
subscriber.add(active_task)
def on_complete():
emit()
subscriber.complete()
def on_finalize():
nonlocal active_task, last_value
last_value = None
active_task = None
sub = source.subscribe(OperatorSubscriber(subscriber, on_next, None, on_complete, on_finalize))
return operate(init)

View File

@ -1,7 +1,8 @@
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions import Subscriber, Observable from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.operator_subscriber import OperatorSubscriber from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.utils import operate from cpl_reactive_extensions.internal.operator_subscriber import OperatorSubscriber
from cpl_reactive_extensions.internal.utils import operate
def take(count: int): def take(count: int):

View File

@ -1,7 +1,7 @@
from cpl_core.type import T from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions import Subscriber, Observable from cpl_reactive_extensions.internal.operator_subscriber import OperatorSubscriber
from cpl_reactive_extensions.operator_subscriber import OperatorSubscriber from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.utils import operate from cpl_reactive_extensions.internal.utils import operate
def take_until(notifier: Observable): def take_until(notifier: Observable):

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
cpl-reactive-extensions CPL Simple ReactiveX implementation
~~~~~~~~~~~~~~~~~~~
CPL Simple ReactiveX implementation, see RxJS and RxPy for detailed implementation.
:copyright: (c) 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_reactive_extensions.scheduler"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2023 sh-edraft.de"
__version__ = "2023.4.dev170"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -0,0 +1,36 @@
from typing import Type
from cpl_reactive_extensions.internal.action import Action
from cpl_reactive_extensions.internal.async_action import AsyncAction
from cpl_reactive_extensions.scheduler.scheduler import Scheduler
class AsyncScheduler(Scheduler):
def __init__(self, scheduler_action_ctor: Type[Action], now=None):
Scheduler.__init__(self, scheduler_action_ctor, now)
self.actions: list[AsyncAction] = []
self._active = False
def flush(self, action: AsyncAction):
if self._active:
self.actions.append(action)
return
error = None
self._active = True
for action in self.actions:
error = action.execute(action.state, action.delay)
if error:
break
self._active = False
if error is not None:
for action in self.actions:
action.unsubscribe()
raise error
async_scheduler = AsyncScheduler(AsyncAction)

View File

@ -0,0 +1,29 @@
from datetime import datetime
from typing import Callable, Optional, Type
from cpl_core.type import T, Number
from cpl_reactive_extensions.abc.scheduler_action import SchedulerAction
from cpl_reactive_extensions.abc.scheduler_like import SchedulerLike
from cpl_reactive_extensions.internal.action import Action
from cpl_reactive_extensions.internal.subscription import Subscription
class Scheduler(SchedulerLike):
@staticmethod
@property
def _get_now(self=None) -> Number:
return int(datetime.now().strftime("%s"))
now = _get_now
def __init__(self, scheduler_action_ctor: Type[Action], now=None):
self.now = self._get_now if now is None else now
self._scheduler_action_ctor = scheduler_action_ctor
def schedule(
self, work: Callable[[SchedulerAction, Optional[T]], None], delay: Number, state: T = None
) -> Subscription:
action = self._scheduler_action_ctor(self, work)
x = action.schedule(state, delay)
return x

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
cpl-reactive-extensions CPL Simple ReactiveX implementation
~~~~~~~~~~~~~~~~~~~
CPL Simple ReactiveX implementation, see RxJS and RxPy for detailed implementation.
:copyright: (c) 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_reactive_extensions.subject"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2023 sh-edraft.de"
__version__ = "2023.4.dev170"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="4", micro="dev170")

View File

@ -1,9 +1,11 @@
from typing import Type
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions.subject import Subject from cpl_reactive_extensions.subject.subject import Subject
class BehaviorSubject(Subject): class BehaviorSubject(Subject):
def __init__(self, _t: type, value: T): def __init__(self, _t: Type[T], value: T):
Subject.__init__(self, _t) Subject.__init__(self, _t)
if not isinstance(value, _t): if not isinstance(value, _t):

View File

@ -4,8 +4,8 @@ from typing import Any, Optional, Type
from cpl_core.type import T from cpl_core.type import T
from cpl_reactive_extensions.abc.observer import Observer from cpl_reactive_extensions.abc.observer import Observer
from cpl_reactive_extensions.observable import Observable from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.subscriber import Subscriber from cpl_reactive_extensions.internal.subscriber import Subscriber
from cpl_reactive_extensions.subscription import Subscription from cpl_reactive_extensions.internal.subscription import Subscription
class Subject(Observable, Observer): class Subject(Observable, Observer):

View File

@ -0,0 +1,24 @@
import threading
import time
from typing import Callable
from cpl_core.type import Number
class Timer:
def __init__(self, interval: Number, action: Callable, *args):
self._interval = interval / 1000
self._action = action
self._args = args
self.stop_event = threading.Event()
thread = threading.Thread(target=self.__set_interval)
thread.start()
def __set_interval(self):
next_time = time.time() + self._interval
while not self.stop_event.wait(next_time - time.time()):
next_time += self._interval
self._action(*self._args)
def clear(self):
self.stop_event.set()

View File

@ -3,7 +3,9 @@ import traceback
import unittest import unittest
from cpl_core.console import Console from cpl_core.console import Console
from cpl_reactive_extensions import Subject from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.operators import debounce_time
from cpl_reactive_extensions.subject.subject import Subject
from cpl_reactive_extensions.interval import Interval from cpl_reactive_extensions.interval import Interval
from cpl_reactive_extensions.operators.take import take from cpl_reactive_extensions.operators.take import take
from cpl_reactive_extensions.operators.take_until import take_until from cpl_reactive_extensions.operators.take_until import take_until
@ -65,3 +67,17 @@ class ObservableOperatorTestCase(unittest.TestCase):
unsubscriber.next(None) unsubscriber.next(None)
unsubscriber.complete() unsubscriber.complete()
self.assertEqual(count, timer * 10 - 1) self.assertEqual(count, timer * 10 - 1)
def test_debounce_time(self):
def call(x):
x.next(1)
x.next(2)
x.next(3)
x.next(4)
x.next(5)
x.next(6)
x.complete()
observable = Observable(call)
observable.pipe(debounce_time(600)).subscribe(lambda x: Console.write_line("Hey", x))

View File

@ -5,11 +5,11 @@ from datetime import datetime
from threading import Timer from threading import Timer
from cpl_core.console import Console from cpl_core.console import Console
from cpl_reactive_extensions.behavior_subject import BehaviorSubject from cpl_reactive_extensions.subject.behavior_subject import BehaviorSubject
from cpl_reactive_extensions.interval import Interval from cpl_reactive_extensions.interval import Interval
from cpl_reactive_extensions.observable import Observable from cpl_reactive_extensions.observable import Observable
from cpl_reactive_extensions.subject import Subject from cpl_reactive_extensions.subject.subject import Subject
from cpl_reactive_extensions.subscriber import Observer from cpl_reactive_extensions.internal.subscriber import Observer
class ReactiveTestCase(unittest.TestCase): class ReactiveTestCase(unittest.TestCase):

View File

@ -1,7 +1,8 @@
import unittest import unittest
from unittests_reactive_extenstions.observable_operator import ObservableOperatorTestCase from unittests_reactive_extenstions.observable_operator_test_case import ObservableOperatorTestCase
from unittests_reactive_extenstions.reactive_test_case import ReactiveTestCase from unittests_reactive_extenstions.reactive_test_case import ReactiveTestCase
from unittests_reactive_extenstions.scheduler_test_case import SchedulerTestCase
class ReactiveTestSuite(unittest.TestSuite): class ReactiveTestSuite(unittest.TestSuite):
@ -11,6 +12,7 @@ class ReactiveTestSuite(unittest.TestSuite):
loader = unittest.TestLoader() loader = unittest.TestLoader()
self.addTests(loader.loadTestsFromTestCase(ReactiveTestCase)) self.addTests(loader.loadTestsFromTestCase(ReactiveTestCase))
self.addTests(loader.loadTestsFromTestCase(ObservableOperatorTestCase)) self.addTests(loader.loadTestsFromTestCase(ObservableOperatorTestCase))
self.addTests(loader.loadTestsFromTestCase(SchedulerTestCase))
def run(self, *args): def run(self, *args):
super().run(*args) super().run(*args)

View File

@ -0,0 +1,36 @@
import time
import unittest
from datetime import datetime
from cpl_core.console import Console
from cpl_reactive_extensions.scheduler.async_scheduler import async_scheduler
from cpl_reactive_extensions.timer import Timer
class SchedulerTestCase(unittest.TestCase):
def setUp(self):
pass
def test_timer(self):
count = 0
def task():
nonlocal count
Console.write_line(datetime.now(), "Hello world")
count += 1
timer = Timer(100, task)
time.sleep(0.25)
self.assertEqual(count, 2)
timer.clear()
def test_schedule(self):
count = 0
def task():
nonlocal count
Console.write_line(datetime.now(), "Hello world")
count += 1
async_scheduler.schedule(task, 100)
time.sleep(2)

View File

@ -0,0 +1,21 @@
import time
from datetime import datetime
from cpl_core.console import Console
from cpl_reactive_extensions.timer import Timer
def test_timer():
is_working = False
def task():
nonlocal is_working
Console.write_line(datetime.now(), "Hello world")
is_working = True
timer = Timer(100, task)
time.sleep(0.2)
timer.clear()
test_timer()

View File

@ -2,9 +2,9 @@
"ProjectSettings": { "ProjectSettings": {
"Name": "unittests_reactive_extenstions", "Name": "unittests_reactive_extenstions",
"Version": { "Version": {
"Major": "0", "Major": "2023",
"Minor": "0", "Minor": "4",
"Micro": "0" "Micro": "dev170"
}, },
"Author": "", "Author": "",
"AuthorEmail": "", "AuthorEmail": "",
@ -16,7 +16,7 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"cpl-core>=2023.4.0" "cpl-core>=2023.4.dev170"
], ],
"DevDependencies": [ "DevDependencies": [
"cpl-cli>=2023.4.0" "cpl-cli>=2023.4.0"