venv added, updated

This commit is contained in:
Norbert
2024-09-13 09:46:28 +02:00
parent 577596d9f3
commit 82af8c809a
4812 changed files with 640223 additions and 2 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
__version__ = "4.0.4"

View File

@@ -0,0 +1,22 @@
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",
]

View File

@@ -0,0 +1,34 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from types import TracebackType
from typing import Optional, Type
class DisposableBase(ABC):
"""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
def __enter__(self) -> DisposableBase:
"""Context management protocol."""
return self
def __exit__(
self,
exctype: Optional[Type[BaseException]],
excinst: Optional[BaseException],
exctb: Optional[TracebackType],
) -> None:
"""Context management protocol."""
self.dispose()
__all__ = ["DisposableBase"]

View File

@@ -0,0 +1,45 @@
from abc import ABC, abstractmethod
from typing import Callable, Generic, Optional, TypeVar, Union
from .disposable import DisposableBase
from .observer import ObserverBase, OnCompleted, OnError, OnNext
from .scheduler import SchedulerBase
_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,
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:
"""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"]

View File

@@ -0,0 +1,48 @@
from abc import ABC, abstractmethod
from typing import Callable, Generic, TypeVar
_T = TypeVar("_T")
_T_in = TypeVar("_T_in", contravariant=True)
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: _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
__all__ = ["ObserverBase", "OnNext", "OnError", "OnCompleted"]

View File

@@ -0,0 +1,49 @@
from abc import ABC, abstractmethod
from typing import Callable, Optional, TypeVar, Union
from .disposable import DisposableBase
from .scheduler import RelativeTime, ScheduledAction
_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: 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",
]

View File

@@ -0,0 +1,152 @@
from abc import ABC, abstractmethod
from datetime import datetime, timedelta
from typing import Callable, Optional, TypeVar, Union
from .disposable import DisposableBase
_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) -> 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[_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: 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: 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: 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
__all__ = [
"SchedulerBase",
"AbsoluteTime",
"RelativeTime",
"AbsoluteOrRelativeTime",
"ScheduledAction",
]

View File

@@ -0,0 +1,14 @@
from abc import ABC, abstractmethod
class StartableBase(ABC):
"""Abstract base class for Thread- and Process-like objects."""
__slots__ = ()
@abstractmethod
def start(self) -> None:
raise NotImplementedError
__all__ = ["StartableBase"]

View File

@@ -0,0 +1,72 @@
from abc import abstractmethod
from typing import Optional, TypeVar, Union
from .disposable import DisposableBase
from .observable import ObservableBase
from .observer import ObserverBase, OnCompleted, OnError, OnNext
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.
"""
__slots__ = ()
@abstractmethod
def subscribe(
self,
on_next: Optional[Union[OnNext[_T], ObserverBase[_T]]] = None,
on_error: Optional[OnError] = None,
on_completed: Optional[OnCompleted] = 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_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_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
__all__ = ["SubjectBase"]

View File

@@ -0,0 +1,19 @@
from .booleandisposable import BooleanDisposable
from .compositedisposable import CompositeDisposable
from .disposable import Disposable
from .multipleassignmentdisposable import MultipleAssignmentDisposable
from .refcountdisposable import RefCountDisposable
from .scheduleddisposable import ScheduledDisposable
from .serialdisposable import SerialDisposable
from .singleassignmentdisposable import SingleAssignmentDisposable
__all__ = [
"BooleanDisposable",
"CompositeDisposable",
"Disposable",
"MultipleAssignmentDisposable",
"RefCountDisposable",
"ScheduledDisposable",
"SerialDisposable",
"SingleAssignmentDisposable",
]

View File

@@ -0,0 +1,20 @@
from threading import RLock
from reactivex.abc import DisposableBase
class BooleanDisposable(DisposableBase):
"""Represents a Disposable that can be checked for status."""
def __init__(self) -> None:
"""Initializes a new instance of the BooleanDisposable class."""
self.is_disposed = False
self.lock = RLock()
super().__init__()
def dispose(self) -> None:
"""Sets the status to disposed"""
self.is_disposed = True

View File

@@ -0,0 +1,103 @@
from threading import RLock
from typing import Any, List
from reactivex import abc
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[abc.DisposableBase] = args[0]
else:
self.disposable = list(args)
self.is_disposed = False
self.lock = RLock()
super(CompositeDisposable, self).__init__()
def add(self, item: abc.DisposableBase) -> None:
"""Adds a disposable to the CompositeDisposable or disposes the
disposable if the CompositeDisposable is disposed
Args:
item: Disposable to add."""
should_dispose = False
with self.lock:
if self.is_disposed:
should_dispose = True
else:
self.disposable.append(item)
if should_dispose:
item.dispose()
def remove(self, item: abc.DisposableBase) -> bool:
"""Removes and disposes the first occurrence of a disposable
from the CompositeDisposable."""
if self.is_disposed:
return False
should_dispose = False
with self.lock:
if item in self.disposable:
self.disposable.remove(item)
should_dispose = True
if should_dispose:
item.dispose()
return should_dispose
def dispose(self) -> None:
"""Disposes all disposable in the group and removes them from
the group."""
if self.is_disposed:
return
with self.lock:
self.is_disposed = True
current_disposable = self.disposable
self.disposable = []
for disp in current_disposable:
disp.dispose()
def clear(self) -> None:
"""Removes and disposes all disposable from the
CompositeDisposable, but does not dispose the
CompositeDisposable."""
with self.lock:
current_disposable = self.disposable
self.disposable = []
for disposable in current_disposable:
disposable.dispose()
def contains(self, item: abc.DisposableBase) -> bool:
"""Determines whether the CompositeDisposable contains a specific
disposable.
Args:
item: Disposable to search for
Returns:
True if the disposable was found; otherwise, False"""
return item in self.disposable
def to_list(self) -> List[abc.DisposableBase]:
return self.disposable[:]
def __len__(self) -> int:
return len(self.disposable)
@property
def length(self) -> int:
return len(self.disposable)

View File

@@ -0,0 +1,43 @@
from threading import RLock
from typing import Optional
from reactivex import typing
from reactivex.abc import DisposableBase
from reactivex.internal import noop
from reactivex.typing import Action
class Disposable(DisposableBase):
"""Main disposable class"""
def __init__(self, action: Optional[typing.Action] = None) -> None:
"""Creates a disposable object that invokes the specified
action when disposed.
Args:
action: Action to run during the first call to dispose.
The action is guaranteed to be run at most once.
Returns:
The disposable object that runs the given action upon
disposal.
"""
self.is_disposed = False
self.action: Action = action or noop
self.lock = RLock()
super().__init__()
def dispose(self) -> None:
"""Performs the task of cleaning up resources."""
dispose = False
with self.lock:
if not self.is_disposed:
dispose = True
self.is_disposed = True
if dispose:
self.action()

View File

@@ -0,0 +1,49 @@
from threading import RLock
from typing import Optional
from reactivex.abc import DisposableBase
class MultipleAssignmentDisposable(DisposableBase):
"""Represents a disposable resource whose underlying disposable
resource can be replaced by another disposable resource."""
def __init__(self) -> None:
self.current: Optional[DisposableBase] = None
self.is_disposed = False
self.lock = RLock()
super().__init__()
def get_disposable(self) -> Optional[DisposableBase]:
return self.current
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."""
with self.lock:
should_dispose = self.is_disposed
if not should_dispose:
self.current = value
if should_dispose and value is not None:
value.dispose()
disposable = property(get_disposable, set_disposable)
def dispose(self) -> None:
"""Disposes the underlying disposable as well as all future
replacements."""
old = None
with self.lock:
if not self.is_disposed:
self.is_disposed = True
old = self.current
self.current = None
if old is not None:
old.dispose()

View File

@@ -0,0 +1,82 @@
from threading import RLock
from typing import Optional
from reactivex.abc import DisposableBase
from .disposable import 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(DisposableBase):
def __init__(self, parent: "RefCountDisposable") -> None:
self.parent: Optional[RefCountDisposable] = parent
self.is_disposed = False
self.lock = RLock()
def dispose(self) -> None:
with self.lock:
parent = self.parent
self.parent = 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."""
self.underlying_disposable = disposable
self.is_primary_disposed = False
self.is_disposed = False
self.lock = RLock()
self.count = 0
super().__init__()
def dispose(self) -> None:
"""Disposes the underlying disposable only when all dependent
disposable have been disposed."""
if self.is_disposed:
return
underlying_disposable = None
with self.lock:
if not self.is_primary_disposed:
self.is_primary_disposed = True
if not self.count:
self.is_disposed = True
underlying_disposable = self.underlying_disposable
if underlying_disposable is not None:
underlying_disposable.dispose()
def release(self) -> None:
if self.is_disposed:
return
should_dispose = False
with self.lock:
self.count -= 1
if not self.count and self.is_primary_disposed:
self.is_disposed = True
should_dispose = True
if should_dispose:
self.underlying_disposable.dispose()
@property
def disposable(self) -> DisposableBase:
"""Returns a dependent disposable that when disposed decreases the
refcount on the underlying disposable."""
with self.lock:
if self.is_disposed:
return Disposable()
self.count += 1
return self.InnerDisposable(self)

View File

@@ -0,0 +1,38 @@
from threading import RLock
from typing import Any
from reactivex import abc
from .singleassignmentdisposable import SingleAssignmentDisposable
class ScheduledDisposable(abc.DisposableBase):
"""Represents a disposable resource whose disposal invocation will
be scheduled on the specified Scheduler"""
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."""
self.scheduler = scheduler
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."""
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
"""Scheduled dispose action"""
self.disposable.dispose()
self.scheduler.schedule(action)

View File

@@ -0,0 +1,58 @@
from threading import RLock
from typing import Optional
from reactivex import abc
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[abc.DisposableBase] = None
self.is_disposed = False
self.lock = RLock()
super().__init__()
def get_disposable(self) -> Optional[abc.DisposableBase]:
return self.current
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[abc.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 is not None:
value.dispose()
disposable = property(get_disposable, set_disposable)
def dispose(self) -> None:
"""Disposes the underlying disposable as well as all future
replacements."""
old: Optional[abc.DisposableBase] = None
with self.lock:
if not self.is_disposed:
self.is_disposed = True
old = self.current
self.current = None
if old is not None:
old.dispose()

View File

@@ -0,0 +1,53 @@
from threading import RLock
from typing import Optional
from reactivex.abc import DisposableBase
class SingleAssignmentDisposable(DisposableBase):
"""Single assignment disposable.
Represents a disposable resource which only allows a single
assignment of its underlying disposable resource. If an underlying
disposable resource has already been set, future attempts to set the
underlying disposable resource will throw an Error."""
def __init__(self) -> None:
"""Initializes a new instance of the SingleAssignmentDisposable
class.
"""
self.is_disposed: bool = False
self.current: Optional[DisposableBase] = None
self.lock = RLock()
super().__init__()
def get_disposable(self) -> Optional[DisposableBase]:
return self.current
def set_disposable(self, value: DisposableBase) -> None:
if self.current:
raise Exception("Disposable has already been assigned")
with self.lock:
should_dispose = self.is_disposed
if not should_dispose:
self.current = value
if self.is_disposed and value:
value.dispose()
disposable = property(get_disposable, set_disposable)
def dispose(self) -> None:
"""Sets the status to disposed"""
old: Optional[DisposableBase] = None
with self.lock:
if not self.is_disposed:
self.is_disposed = True
old = self.current
self.current = None
if old is not None:
old.dispose()

View File

@@ -0,0 +1,29 @@
from .basic import default_comparer, default_error, noop
from .concurrency import default_thread_factory, synchronized
from .constants import DELTA_ZERO, UTC_ZERO
from .exceptions import (
ArgumentOutOfRangeException,
DisposedException,
SequenceContainsNoElementsError,
)
from .priorityqueue import PriorityQueue
from .utils import NotSet, add_ref, alias, infinite
__all__ = [
"add_ref",
"alias",
"ArgumentOutOfRangeException",
"DisposedException",
"default_comparer",
"default_error",
"infinite",
"noop",
"NotSet",
"SequenceContainsNoElementsError",
"concurrency",
"DELTA_ZERO",
"UTC_ZERO",
"synchronized",
"default_thread_factory",
"PriorityQueue",
]

View File

@@ -0,0 +1,36 @@
from datetime import datetime
from typing import Any, NoReturn, TypeVar, Union
_T = TypeVar("_T")
def noop(*args: Any, **kw: Any) -> None:
"""No operation. Returns nothing"""
def identity(x: _T) -> _T:
"""Returns argument x"""
return x
def default_now() -> datetime:
return datetime.utcnow()
def default_comparer(x: _T, y: _T) -> bool:
return x == y
def default_sub_comparer(x: Any, y: Any) -> Any:
return x - y
def default_key_serializer(x: Any) -> str:
return str(x)
def default_error(err: Union[Exception, str]) -> NoReturn:
if isinstance(err, BaseException):
raise err
raise Exception(err)

View File

@@ -0,0 +1,26 @@
from threading import RLock, Thread
from typing import Any, Callable, TypeVar
from typing_extensions import ParamSpec
from reactivex.typing import StartableTarget
_T = TypeVar("_T")
_P = ParamSpec("_P")
def default_thread_factory(target: StartableTarget) -> Thread:
return Thread(target=target, daemon=True)
def synchronized(lock: RLock) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]:
"""A decorator for synchronizing access to a given function."""
def wrapper(fn: Callable[_P, _T]) -> Callable[_P, _T]:
def inner(*args: _P.args, **kw: _P.kwargs) -> Any:
with lock:
return fn(*args, **kw)
return inner
return wrapper

View File

@@ -0,0 +1,4 @@
from datetime import datetime, timedelta
DELTA_ZERO = timedelta(0)
UTC_ZERO = datetime.utcfromtimestamp(0)

View File

@@ -0,0 +1,36 @@
# Rx Exceptions
from typing import Optional
class SequenceContainsNoElementsError(Exception):
def __init__(self, msg: Optional[str] = None):
super().__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"
)
class DisposedException(Exception):
def __init__(self, msg: Optional[str] = None):
super().__init__(msg or "Object has been disposed")
class ReEntracyException(Exception):
def __init__(self, msg: Optional[str] = None):
super().__init__(msg or "Re-entrancy detected")
class CompletedException(Exception):
def __init__(self, msg: Optional[str] = None):
super().__init__(msg or "Observer completed")
class WouldBlockException(Exception):
def __init__(self, msg: Optional[str] = None):
super().__init__(msg or "Would block")

View File

@@ -0,0 +1,54 @@
import heapq
from sys import maxsize
from typing import Generic, List, Tuple, TypeVar
_T1 = TypeVar("_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.count = PriorityQueue.MIN_COUNT # Monotonic increasing for sort stability
def __len__(self) -> int:
"""Returns length of queue"""
return len(self.items)
def peek(self) -> _T1:
"""Returns first item in queue without removing it"""
return self.items[0][0]
def dequeue(self) -> _T1:
"""Returns and removes item with lowest priority from queue"""
item: _T1 = heapq.heappop(self.items)[0]
if not self.items:
self.count = PriorityQueue.MIN_COUNT
return item
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:
"""Remove given item from queue"""
for index, _item in enumerate(self.items):
if _item[0] == item:
self.items.pop(index)
heapq.heapify(self.items)
return True
return False
def clear(self) -> None:
"""Remove all items from the queue."""
self.items = []
self.count = PriorityQueue.MIN_COUNT

View File

@@ -0,0 +1,61 @@
from functools import update_wrapper
from types import FunctionType
from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, TypeVar, cast
from typing_extensions import ParamSpec
from reactivex import abc
from reactivex.disposable import CompositeDisposable
from reactivex.disposable.refcountdisposable import RefCountDisposable
if TYPE_CHECKING:
from reactivex import Observable
_T = TypeVar("_T")
_P = ParamSpec("_P")
def add_ref(xs: "Observable[_T]", r: RefCountDisposable) -> "Observable[_T]":
from reactivex import Observable
def subscribe(
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
) -> abc.DisposableBase:
return CompositeDisposable(r.disposable, xs.subscribe(observer))
return Observable(subscribe)
def infinite() -> Iterable[int]:
n = 0
while True:
yield n
n += 1
def alias(name: str, doc: str, fun: Callable[_P, _T]) -> Callable[_P, _T]:
# Adapted from
# https://stackoverflow.com/questions/13503079/how-to-create-a-copy-of-a-python-function#
# See also help(type(lambda: 0))
_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
alias_.__annotations__ = _fun.__annotations__
return cast(Callable[_P, _T], alias_)
class NotSet:
"""Sentinel value."""
def __eq__(self, other: Any) -> bool:
return self is other
def __repr__(self) -> str:
return "NotSet"
__all__ = ["add_ref", "infinite", "alias", "NotSet"]

View File

@@ -0,0 +1,205 @@
from abc import abstractmethod
from typing import Any, Callable, Generic, Optional, TypeVar, Union
from reactivex import abc, typing
from reactivex.scheduler import ImmediateScheduler
from .observable import Observable
from .observer import Observer
_T = TypeVar("_T")
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: Optional[_T] = None
self.kind: str = ""
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.
Examples:
>>> notification.accept(observer)
>>> notification.accept(on_next, on_error, on_completed)
Args:
on_next: Delegate to invoke for an OnNext notification.
on_error: [Optional] Delegate to invoke for an OnError
notification.
on_completed: [Optional] Delegate to invoke for an
OnCompleted notification.
Returns:
Result produced by the observation."""
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: typing.OnNext[_T],
on_error: Optional[typing.OnError],
on_completed: Optional[typing.OnCompleted],
) -> None:
raise NotImplementedError
@abstractmethod
def _accept_observer(self, observer: abc.ObserverBase[_T]) -> None:
raise NotImplementedError
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.
Args:
scheduler: [Optional] Scheduler to send out the
notification calls on.
Returns:
An observable sequence that surfaces the behavior of the
notification upon subscription.
"""
_scheduler = scheduler or ImmediateScheduler.singleton()
def subscribe(
observer: abc.ObserverBase[_T],
scheduler: Optional[abc.SchedulerBase] = None,
) -> abc.DisposableBase:
def action(scheduler: abc.SchedulerBase, state: Any) -> None:
self._accept_observer(observer)
if self.kind == "N":
observer.on_completed()
__scheduler = scheduler or _scheduler
return __scheduler.schedule(action)
return Observable(subscribe)
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)
return str(self) == other_string
def __eq__(self, other: Any) -> bool:
return self.equals(other)
class OnNext(Notification[_T]):
"""Represents an OnNext notification to an observer."""
def __init__(self, value: _T) -> None:
"""Constructs a notification of a new value."""
super(OnNext, self).__init__()
self.value: _T = value
self.has_value: bool = True
self.kind: str = "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: abc.ObserverBase[_T]) -> None:
return observer.on_next(self.value)
def __str__(self) -> str:
val: Any = self.value
if isinstance(val, int):
val = float(val)
return "OnNext(%s)" % str(val)
class OnError(Notification[_T]):
"""Represents an OnError notification to an observer."""
def __init__(self, error: Union[Exception, str]) -> None:
"""Constructs a notification of an exception."""
super(OnError, self).__init__()
self.exception: Exception = (
error if isinstance(error, Exception) else Exception(error)
)
self.kind = "E"
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: abc.ObserverBase[_T]) -> None:
return observer.on_error(self.exception)
def __str__(self) -> str:
return "OnError(%s)" % str(self.exception)
class OnCompleted(Notification[_T]):
"""Represents an OnCompleted notification to an observer."""
def __init__(self) -> None:
"""Constructs a notification of the end of a sequence."""
super(OnCompleted, self).__init__()
self.kind = "C"
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: abc.ObserverBase[_T]) -> None:
return observer.on_completed()
def __str__(self) -> str:
return "OnCompleted()"
def from_notifier(handler: Callable[[Notification[_T]], None]) -> Observer[_T]:
"""Creates an observer from a notification callback.
Args:
handler: Action that handles a notification.
Returns:
The observer object that invokes the specified handler using
a notification corresponding to each message it receives.
"""
def _on_next(value: _T) -> None:
return handler(OnNext(value))
def _on_error(error: Exception) -> None:
return handler(OnError(error))
def _on_completed() -> None:
return handler(OnCompleted())
return Observer(_on_next, _on_error, _on_completed)

View File

@@ -0,0 +1,5 @@
from .connectableobservable import ConnectableObservable
from .groupedobservable import GroupedObservable
from .observable import Observable
__all__ = ["Observable", "ConnectableObservable", "GroupedObservable"]

View File

@@ -0,0 +1,31 @@
from typing import TypeVar
from reactivex import Observable, never
from reactivex import operators as _
_T = TypeVar("_T")
def amb_(*sources: Observable[_T]) -> Observable[_T]:
"""Propagates the observable sequence that reacts first.
Example:
>>> winner = amb(xs, ys, zs)
Returns:
An observable sequence that surfaces any of the given sequences,
whichever reacted first.
"""
acc: Observable[_T] = never()
def func(previous: Observable[_T], current: Observable[_T]) -> Observable[_T]:
return _.amb(previous)(current)
for source in sources:
acc = func(acc, source)
return acc
__all__ = ["amb_"]

View File

@@ -0,0 +1,35 @@
from asyncio import Future
from typing import Callable, Mapping, Optional, TypeVar, Union
from reactivex import Observable, abc, defer, empty, from_future
_Key = TypeVar("_Key")
_T = TypeVar("_T")
def case_(
mapper: Callable[[], _Key],
sources: Mapping[_Key, Observable[_T]],
default_source: Optional[Union[Observable[_T], "Future[_T]"]] = None,
) -> Observable[_T]:
default_source_: Union[Observable[_T], "Future[_T]"] = default_source or empty()
def factory(_: abc.SchedulerBase) -> Observable[_T]:
try:
result: Union[Observable[_T], "Future[_T]"] = sources[mapper()]
except KeyError:
result = default_source_
if isinstance(result, Future):
result_: Observable[_T] = from_future(result)
else:
result_ = result
return result_
return defer(factory)
__all__ = ["case_"]

View File

@@ -0,0 +1,84 @@
from typing import Any, Iterable, Optional, TypeVar
from reactivex import Observable, abc
from reactivex.disposable import (
CompositeDisposable,
Disposable,
SerialDisposable,
SingleAssignmentDisposable,
)
from reactivex.scheduler import CurrentThreadScheduler
_T = TypeVar("_T")
def catch_with_iterable_(sources: Iterable[Observable[_T]]) -> Observable[_T]:
"""Continues an observable sequence that is terminated by an
exception with the next observable sequence.
Examples:
>>> res = catch([xs, ys, zs])
>>> res = reactivex.catch(src for src in [xs, ys, zs])
Args:
sources: an Iterable of observables. Thus a generator is accepted.
Returns:
An observable sequence containing elements from consecutive
source sequences until a source sequence terminates
successfully.
"""
sources_ = iter(sources)
def subscribe(
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
) -> abc.DisposableBase:
_scheduler = scheduler_ or CurrentThreadScheduler.singleton()
subscription = SerialDisposable()
cancelable = SerialDisposable()
last_exception = None
is_disposed = False
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
def on_error(exn: Exception) -> None:
nonlocal last_exception
last_exception = exn
cancelable.disposable = _scheduler.schedule(action)
if is_disposed:
return
try:
current = next(sources_)
except StopIteration:
if last_exception:
observer.on_error(last_exception)
else:
observer.on_completed()
except Exception as ex: # pylint: disable=broad-except
observer.on_error(ex)
else:
d = SingleAssignmentDisposable()
subscription.disposable = d
d.disposable = current.subscribe(
observer.on_next,
on_error,
observer.on_completed,
scheduler=scheduler_,
)
cancelable.disposable = _scheduler.schedule(action)
def dispose() -> None:
nonlocal is_disposed
is_disposed = True
return CompositeDisposable(subscription, cancelable, Disposable(dispose))
return Observable(subscribe)
__all__ = ["catch_with_iterable_"]

View File

@@ -0,0 +1,76 @@
from typing import Any, List, Optional, Tuple
from reactivex import Observable, abc
from reactivex.disposable import CompositeDisposable, SingleAssignmentDisposable
def combine_latest_(*sources: Observable[Any]) -> Observable[Tuple[Any, ...]]:
"""Merges the specified observable sequences into one observable
sequence by creating a tuple whenever any of the
observable sequences produces an element.
Examples:
>>> obs = combine_latest(obs1, obs2, obs3)
Returns:
An observable sequence containing the result of combining
elements of the sources into a tuple.
"""
parent = sources[0]
def subscribe(
observer: abc.ObserverBase[Any], scheduler: Optional[abc.SchedulerBase] = None
) -> CompositeDisposable:
n = len(sources)
has_value = [False] * n
has_value_all = [False]
is_done = [False] * n
values = [None] * n
def _next(i: Any) -> None:
has_value[i] = True
if has_value_all[0] or all(has_value):
res = tuple(values)
observer.on_next(res)
elif all([x for j, x in enumerate(is_done) if j != i]):
observer.on_completed()
has_value_all[0] = all(has_value)
def done(i: Any) -> None:
is_done[i] = True
if all(is_done):
observer.on_completed()
subscriptions: List[Optional[SingleAssignmentDisposable]] = [None] * n
def func(i: int) -> None:
subscriptions[i] = SingleAssignmentDisposable()
def on_next(x: Any) -> None:
with parent.lock:
values[i] = x
_next(i)
def on_completed() -> None:
with parent.lock:
done(i)
subscription = subscriptions[i]
assert subscription
subscription.disposable = sources[i].subscribe(
on_next, observer.on_error, on_completed, scheduler=scheduler
)
for idx in range(n):
func(idx)
return CompositeDisposable(subscriptions)
return Observable(subscribe)
__all__ = ["combine_latest_"]

View File

@@ -0,0 +1,62 @@
from typing import Any, Iterable, Optional, TypeVar
from reactivex import Observable, abc
from reactivex.disposable import (
CompositeDisposable,
Disposable,
SerialDisposable,
SingleAssignmentDisposable,
)
from reactivex.scheduler import CurrentThreadScheduler
_T = TypeVar("_T")
def concat_with_iterable_(sources: Iterable[Observable[_T]]) -> Observable[_T]:
def subscribe(
observer: abc.ObserverBase[_T], scheduler_: Optional[abc.SchedulerBase] = None
) -> abc.DisposableBase:
_scheduler = scheduler_ or CurrentThreadScheduler.singleton()
sources_ = iter(sources)
subscription = SerialDisposable()
cancelable = SerialDisposable()
is_disposed = False
def action(scheduler: abc.SchedulerBase, state: Any = None) -> None:
nonlocal is_disposed
if is_disposed:
return
def on_completed() -> None:
cancelable.disposable = _scheduler.schedule(action)
try:
current = next(sources_)
except StopIteration:
observer.on_completed()
except Exception as ex: # pylint: disable=broad-except
observer.on_error(ex)
else:
d = SingleAssignmentDisposable()
subscription.disposable = d
d.disposable = current.subscribe(
observer.on_next,
observer.on_error,
on_completed,
scheduler=scheduler_,
)
cancelable.disposable = _scheduler.schedule(action)
def dispose() -> None:
nonlocal is_disposed
is_disposed = True
return CompositeDisposable(subscription, cancelable, Disposable(dispose))
return Observable(subscribe)
__all__ = ["concat_with_iterable_"]

View File

@@ -0,0 +1,82 @@
from typing import List, Optional, TypeVar
from reactivex import abc
from reactivex.disposable import CompositeDisposable, Disposable
from .observable import Observable
_T = TypeVar("_T")
class ConnectableObservable(Observable[_T]):
"""Represents an observable that can be connected and
disconnected."""
def __init__(self, source: abc.ObservableBase[_T], subject: abc.SubjectBase[_T]):
self.subject = subject
self.has_subscription = False
self.subscription: Optional[abc.DisposableBase] = None
self.source = source
super().__init__()
def _subscribe_core(
self,
observer: abc.ObserverBase[_T],
scheduler: Optional[abc.SchedulerBase] = None,
) -> abc.DisposableBase:
return self.subject.subscribe(observer, scheduler=scheduler)
def connect(
self, scheduler: Optional[abc.SchedulerBase] = None
) -> Optional[abc.DisposableBase]:
"""Connects the observable."""
if not self.has_subscription:
self.has_subscription = True
def dispose() -> None:
self.has_subscription = False
subscription = self.source.subscribe(self.subject, scheduler=scheduler)
self.subscription = CompositeDisposable(subscription, Disposable(dispose))
return self.subscription
def auto_connect(self, subscriber_count: int = 1) -> Observable[_T]:
"""Returns an observable sequence that stays connected to the
source indefinitely to the observable sequence.
Providing a subscriber_count will cause it to connect() after
that many subscriptions occur. A subscriber_count of 0 will
result in emissions firing immediately without waiting for
subscribers.
"""
connectable_subscription: List[Optional[abc.DisposableBase]] = [None]
count = [0]
source = self
is_connected = [False]
if subscriber_count == 0:
connectable_subscription[0] = source.connect()
is_connected[0] = True
def subscribe(
observer: abc.ObserverBase[_T],
scheduler: Optional[abc.SchedulerBase] = None,
) -> abc.DisposableBase:
count[0] += 1
should_connect = count[0] == subscriber_count and not is_connected[0]
subscription = source.subscribe(observer)
if should_connect:
connectable_subscription[0] = source.connect(scheduler)
is_connected[0] = True
def dispose() -> None:
subscription.dispose()
count[0] -= 1
is_connected[0] = False
return Disposable(dispose)
return Observable(subscribe)

View File

@@ -0,0 +1,43 @@
from asyncio import Future
from typing import Callable, Optional, TypeVar, Union
from reactivex import Observable, abc, from_future, throw
from reactivex.scheduler import ImmediateScheduler
_T = TypeVar("_T")
def defer_(
factory: Callable[[abc.SchedulerBase], Union[Observable[_T], "Future[_T]"]]
) -> Observable[_T]:
"""Returns an observable sequence that invokes the specified factory
function whenever a new observer subscribes.
Example:
>>> res = defer(lambda scheduler: of(1, 2, 3))
Args:
observable_factory: Observable factory function to invoke for
each observer that subscribes to the resulting sequence. The
factory takes a single argument, the scheduler used.
Returns:
An observable sequence whose observers trigger an invocation
of the given observable factory function.
"""
def subscribe(
observer: abc.ObserverBase[_T], scheduler: Optional[abc.SchedulerBase] = None
) -> abc.DisposableBase:
try:
result = factory(scheduler or ImmediateScheduler.singleton())
except Exception as ex: # By design. pylint: disable=W0703
return throw(ex).subscribe(observer)
result = from_future(result) if isinstance(result, Future) else result
return result.subscribe(observer, scheduler=scheduler)
return Observable(subscribe)
__all__ = ["defer_"]

View File

@@ -0,0 +1,22 @@
from typing import Any, Optional
from reactivex import Observable, abc
from reactivex.scheduler import ImmediateScheduler
def empty_(scheduler: Optional[abc.SchedulerBase] = None) -> Observable[Any]:
def subscribe(
observer: abc.ObserverBase[Any], scheduler_: Optional[abc.SchedulerBase] = None
) -> abc.DisposableBase:
_scheduler = scheduler or scheduler_ or ImmediateScheduler.singleton()
def action(_: abc.SchedulerBase, __: Any) -> None:
observer.on_completed()
return _scheduler.schedule(action)
return Observable(subscribe)
__all__ = ["empty_"]

Some files were not shown because too many files have changed in this diff Show More