├── __init__.py ├── addon_updater.py ├── addon_updater_ops.py ├── icons ├── icon-build.png ├── icon-fill.png ├── icon-paint.png ├── icon-setnormal.png ├── sprytile.build_tool.dat ├── sprytile.fill_tool.dat └── sprytile.paint_tool.dat ├── license.txt ├── rx ├── __init__.py ├── backpressure │ ├── __init__.py │ ├── controlledobservable.py │ ├── controlledsubject.py │ ├── pausable.py │ ├── pausablebuffered.py │ ├── stopandwait.py │ ├── stopandwaitobservable.py │ ├── windowed.py │ └── windowedobservable.py ├── concurrency │ ├── __init__.py │ ├── catchscheduler.py │ ├── currentthreadscheduler.py │ ├── eventloopscheduler.py │ ├── historicalscheduler.py │ ├── immediatescheduler.py │ ├── mainloopscheduler │ │ ├── __init__.py │ │ ├── asyncioscheduler.py │ │ ├── eventletscheduler.py │ │ ├── geventscheduler.py │ │ ├── gtkscheduler.py │ │ ├── ioloopscheduler.py │ │ ├── pygamescheduler.py │ │ ├── qtscheduler.py │ │ ├── tkinterscheduler.py │ │ ├── twistedscheduler.py │ │ └── wxscheduler.py │ ├── newthreadscheduler.py │ ├── scheduleditem.py │ ├── scheduleperiodic.py │ ├── schedulerbase.py │ ├── threadpoolscheduler.py │ ├── timeoutscheduler.py │ └── virtualtimescheduler.py ├── core │ ├── __init__.py │ ├── anonymousobservable.py │ ├── anonymousobserver.py │ ├── autodetachobserver.py │ ├── blockingobservable.py │ ├── checkedobserver.py │ ├── disposableextensions.py │ ├── notification.py │ ├── observablebase.py │ ├── observeonobserver.py │ ├── observerbase.py │ ├── observerextensions.py │ ├── py2 │ │ ├── __init__.py │ │ ├── disposable.py │ │ ├── observable.py │ │ ├── observer.py │ │ └── scheduler.py │ ├── py3 │ │ ├── __init__.py │ │ ├── disposable.py │ │ ├── observable.py │ │ ├── observer.py │ │ └── scheduler.py │ └── scheduledobserver.py ├── disposables │ ├── __init__.py │ ├── anonymousdisposable.py │ ├── booleandisposable.py │ ├── compositedisposable.py │ ├── multipleassignmentdisposable.py │ ├── refcountdisposable.py │ ├── scheduleddisposable.py │ ├── serialdisposable.py │ └── singleassignmentdisposable.py ├── internal │ ├── __init__.py │ ├── basic.py │ ├── concurrency.py │ ├── enumerable.py │ ├── enumerator.py │ ├── exceptions.py │ ├── extensionmethod.py │ ├── priorityqueue.py │ └── utils.py ├── joins │ ├── __init__.py │ ├── activeplan.py │ ├── joinobserver.py │ ├── pattern.py │ └── plan.py ├── linq │ ├── __init__.py │ ├── connectableobservable.py │ ├── enumerable │ │ ├── __init__.py │ │ └── whiledo.py │ ├── groupedobservable.py │ └── observable │ │ ├── __init__.py │ │ ├── all.py │ │ ├── amb.py │ │ ├── and_.py │ │ ├── asobservable.py │ │ ├── average.py │ │ ├── blocking │ │ ├── __init__.py │ │ ├── first.py │ │ ├── foreach.py │ │ ├── last.py │ │ └── toiterable.py │ │ ├── buffer.py │ │ ├── bufferwithtime.py │ │ ├── bufferwithtimeorcount.py │ │ ├── case.py │ │ ├── catch.py │ │ ├── combinelatest.py │ │ ├── concat.py │ │ ├── contains.py │ │ ├── count.py │ │ ├── create.py │ │ ├── debounce.py │ │ ├── defaultifempty.py │ │ ├── defer.py │ │ ├── delay.py │ │ ├── delaysubscription.py │ │ ├── delaywithselector.py │ │ ├── dematerialize.py │ │ ├── distinct.py │ │ ├── distinctuntilchanged.py │ │ ├── doaction.py │ │ ├── dowhile.py │ │ ├── elementat.py │ │ ├── elementatordefault.py │ │ ├── empty.py │ │ ├── exclusive.py │ │ ├── expand.py │ │ ├── finallyaction.py │ │ ├── find.py │ │ ├── findindex.py │ │ ├── first.py │ │ ├── firstordefault.py │ │ ├── forin.py │ │ ├── fromcallback.py │ │ ├── fromfuture.py │ │ ├── fromiterable.py │ │ ├── generate.py │ │ ├── generatewithrelativetime.py │ │ ├── groupby.py │ │ ├── groupbyuntil.py │ │ ├── groupjoin.py │ │ ├── ifthen.py │ │ ├── ignoreelements.py │ │ ├── interval.py │ │ ├── isempty.py │ │ ├── join.py │ │ ├── last.py │ │ ├── lastordefault.py │ │ ├── let.py │ │ ├── manyselect.py │ │ ├── materialize.py │ │ ├── max.py │ │ ├── maxby.py │ │ ├── merge.py │ │ ├── min.py │ │ ├── minby.py │ │ ├── multicast.py │ │ ├── never.py │ │ ├── observeon.py │ │ ├── of.py │ │ ├── onerrorresumenext.py │ │ ├── pairwise.py │ │ ├── partition.py │ │ ├── pluck.py │ │ ├── publish.py │ │ ├── publishvalue.py │ │ ├── range.py │ │ ├── reduce.py │ │ ├── repeat.py │ │ ├── replay.py │ │ ├── retry.py │ │ ├── returnvalue.py │ │ ├── sample.py │ │ ├── scan.py │ │ ├── select.py │ │ ├── selectmany.py │ │ ├── selectswitch.py │ │ ├── sequenceequal.py │ │ ├── single.py │ │ ├── singleordefault.py │ │ ├── skip.py │ │ ├── skiplast.py │ │ ├── skiplastwithtime.py │ │ ├── skipuntil.py │ │ ├── skipuntilwithtime.py │ │ ├── skipwhile.py │ │ ├── skipwithtime.py │ │ ├── slice.py │ │ ├── some.py │ │ ├── start.py │ │ ├── startasync.py │ │ ├── startswith.py │ │ ├── statistics.py │ │ ├── subscribeon.py │ │ ├── sum.py │ │ ├── switchlatest.py │ │ ├── take.py │ │ ├── takelast.py │ │ ├── takelastbuffer.py │ │ ├── takelastwithtime.py │ │ ├── takeuntil.py │ │ ├── takeuntilwithtime.py │ │ ├── takewhile.py │ │ ├── takewithtime.py │ │ ├── thendo.py │ │ ├── throttlefirst.py │ │ ├── throw.py │ │ ├── timeinterval.py │ │ ├── timeout.py │ │ ├── timeoutwithselector.py │ │ ├── timer.py │ │ ├── timestamp.py │ │ ├── toasync.py │ │ ├── toblocking.py │ │ ├── todict.py │ │ ├── tofuture.py │ │ ├── toiterable.py │ │ ├── tolist.py │ │ ├── toset.py │ │ ├── transduce.py │ │ ├── using.py │ │ ├── when.py │ │ ├── where.py │ │ ├── whiledo.py │ │ ├── window.py │ │ ├── windowwithcount.py │ │ ├── windowwithtime.py │ │ ├── windowwithtimeorcount.py │ │ ├── withlatestfrom.py │ │ ├── zip.py │ │ └── ziparray.py ├── subjects │ ├── __init__.py │ ├── anonymoussubject.py │ ├── asyncsubject.py │ ├── behaviorsubject.py │ ├── innersubscription.py │ ├── replaysubject.py │ └── subject.py └── testing │ ├── __init__.py │ ├── coldobservable.py │ ├── dump.py │ ├── hotobservable.py │ ├── marbles.py │ ├── mockdisposable.py │ ├── mockobserver.py │ ├── reactive_assert.py │ ├── reactivetest.py │ ├── recorded.py │ ├── subscription.py │ └── testscheduler.py ├── sprytile_gui.py ├── sprytile_modal.py ├── sprytile_panel.py ├── sprytile_preview.py ├── sprytile_tools ├── __init__.py ├── tool_build.py ├── tool_fill.py └── tool_paint.py ├── sprytile_utils.py └── sprytile_uv.py /icons/icon-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/icon-build.png -------------------------------------------------------------------------------- /icons/icon-fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/icon-fill.png -------------------------------------------------------------------------------- /icons/icon-paint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/icon-paint.png -------------------------------------------------------------------------------- /icons/icon-setnormal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/icon-setnormal.png -------------------------------------------------------------------------------- /icons/sprytile.build_tool.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/sprytile.build_tool.dat -------------------------------------------------------------------------------- /icons/sprytile.fill_tool.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/sprytile.fill_tool.dat -------------------------------------------------------------------------------- /icons/sprytile.paint_tool.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/icons/sprytile.paint_tool.dat -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2017 Jeiel Dionisio Aranal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /rx/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | import asyncio 3 | except ImportError: 4 | try: 5 | import trollius as asyncio 6 | except ImportError: 7 | asyncio = None 8 | 9 | try: 10 | import threading 11 | except ImportError: 12 | import rx.internal.concurrency as threading 13 | 14 | try: 15 | from asyncio import Future 16 | except ImportError: 17 | try: 18 | from trollius import Future 19 | except ImportError: 20 | Future = None 21 | 22 | 23 | # Rx configuration dictionary 24 | config = { 25 | "concurrency": threading, 26 | "Future": Future, 27 | "Lock": threading.RLock, # Deprecated 28 | "asyncio": asyncio 29 | } 30 | 31 | from .core import Observer, Observable 32 | from .core.anonymousobserver import AnonymousObserver 33 | from .core.anonymousobservable import AnonymousObservable 34 | 35 | from . import backpressure 36 | from . import linq 37 | -------------------------------------------------------------------------------- /rx/backpressure/__init__.py: -------------------------------------------------------------------------------- 1 | from . import controlledobservable 2 | from . import controlledsubject 3 | from . import pausable 4 | from . import pausablebuffered 5 | from . import stopandwait 6 | from . import windowed 7 | from . import windowedobservable 8 | -------------------------------------------------------------------------------- /rx/backpressure/controlledobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, ObservableBase 2 | from rx.internal import extensionmethod 3 | 4 | from .controlledsubject import ControlledSubject 5 | 6 | 7 | class ControlledObservable(ObservableBase): 8 | 9 | def __init__(self, source, enable_queue, scheduler=None): 10 | super(ControlledObservable, self).__init__() 11 | 12 | self.subject = ControlledSubject(enable_queue, scheduler) 13 | self.source = source.multicast(self.subject).ref_count() 14 | 15 | def _subscribe_core(self, observer): 16 | return self.source.subscribe(observer) 17 | 18 | def request(self, number_of_items): 19 | if number_of_items is None: 20 | number_of_items = -1 21 | return self.subject.request(number_of_items) 22 | 23 | 24 | @extensionmethod(Observable) 25 | def controlled(self, enable_queue=True, scheduler=None): 26 | """Attach a controller to the observable sequence 27 | 28 | Attach a controller to the observable sequence with the ability to 29 | queue. 30 | 31 | Example: 32 | source = rx.Observable.interval(100).controlled() 33 | source.request(3) # Reads 3 values 34 | 35 | Keyword arguments: 36 | :param bool enable_queue: truthy value to determine if values should 37 | be queued pending the next request 38 | :param Scheduler scheduler: determines how the requests will be scheduled 39 | :returns: The observable sequence which only propagates values on request. 40 | :rtype: Observable 41 | """ 42 | 43 | return ControlledObservable(self, enable_queue, scheduler) 44 | -------------------------------------------------------------------------------- /rx/backpressure/pausable.py: -------------------------------------------------------------------------------- 1 | 2 | from rx.core import Observable, ObservableBase, Disposable 3 | from rx.internal import extensionmethod 4 | from rx.disposables import CompositeDisposable 5 | from rx.subjects import Subject 6 | 7 | 8 | class PausableObservable(ObservableBase): 9 | def __init__(self, source, pauser=None): 10 | self.source = source 11 | self.controller = Subject() 12 | 13 | if pauser and hasattr(pauser, "subscribe"): 14 | self.pauser = self.controller.merge(pauser) 15 | else: 16 | self.pauser = self.controller 17 | 18 | super(PausableObservable, self).__init__() 19 | 20 | def _subscribe_core(self, observer): 21 | conn = self.source.publish() 22 | subscription = conn.subscribe(observer) 23 | connection = [Disposable.empty()] 24 | 25 | def on_next(b): 26 | if b: 27 | connection[0] = conn.connect() 28 | else: 29 | connection[0].dispose() 30 | connection[0] = Disposable.empty() 31 | 32 | pausable = self.pauser.distinct_until_changed().subscribe(on_next) 33 | return CompositeDisposable(subscription, connection[0], pausable) 34 | 35 | def pause(self): 36 | self.controller.on_next(False) 37 | 38 | def resume(self): 39 | self.controller.on_next(True) 40 | 41 | 42 | @extensionmethod(Observable) 43 | def pausable(self, pauser): 44 | """Pauses the underlying observable sequence based upon the observable 45 | sequence which yields True/False. 46 | 47 | Example: 48 | pauser = rx.Subject() 49 | source = rx.Observable.interval(100).pausable(pauser) 50 | 51 | Keyword parameters: 52 | pauser -- {Observable} The observable sequence used to pause the 53 | underlying sequence. 54 | 55 | Returns the observable {Observable} sequence which is paused based upon 56 | the pauser. 57 | """ 58 | 59 | return PausableObservable(self, pauser) 60 | -------------------------------------------------------------------------------- /rx/backpressure/stopandwait.py: -------------------------------------------------------------------------------- 1 | from rx.internal import extensionmethod 2 | 3 | from .controlledobservable import ControlledObservable 4 | from .stopandwaitobservable import StopAndWaitObservable 5 | 6 | 7 | @extensionmethod(ControlledObservable) 8 | def stop_and_wait(self): 9 | """Attaches a stop and wait observable to the current observable. 10 | 11 | :returns: A stop and wait observable. 12 | :rtype: Observable 13 | """ 14 | 15 | return StopAndWaitObservable(self) 16 | -------------------------------------------------------------------------------- /rx/backpressure/stopandwaitobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observer, ObservableBase 2 | 3 | from rx.internal.utils import check_disposed 4 | from rx.concurrency import timeout_scheduler 5 | 6 | 7 | class StopAndWaitObserver(Observer): 8 | 9 | def __init__(self, observer, observable, cancel, scheduler=None): 10 | super(StopAndWaitObserver, self).__init__() 11 | 12 | self.scheduler = scheduler 13 | self.observer = observer 14 | self.observable = observable 15 | self.cancel = cancel 16 | self.is_disposed = False 17 | 18 | def on_completed(self): 19 | check_disposed(self) 20 | 21 | self.observer.on_completed() 22 | self.dispose() 23 | 24 | def on_error(self, error): 25 | check_disposed(self) 26 | 27 | self.observer.on_error(error) 28 | self.dispose() 29 | 30 | def on_next(self, value): 31 | check_disposed(self) 32 | 33 | self.observer.on_next(value) 34 | 35 | def action(scheduler, state): 36 | self.observable.source.request(1) 37 | self.scheduler.schedule(action) 38 | 39 | def dispose(self): 40 | self.observer = None 41 | if self.cancel: 42 | self.cancel.dispose() 43 | self.cancel = None 44 | 45 | self.is_disposed = True 46 | 47 | 48 | class StopAndWaitObservable(ObservableBase): 49 | 50 | def __init__(self, source, scheduler=None): 51 | super(StopAndWaitObservable, self).__init__() 52 | self.scheduler = scheduler or timeout_scheduler 53 | self.source = source 54 | self.subscription = None 55 | 56 | def _subscribe_core(self, observer): 57 | observer = StopAndWaitObserver(observer, self, self.subscription, self.scheduler) 58 | self.subscription = self.source.subscribe(observer) 59 | 60 | def action(scheduler, state=None): 61 | self.source.request(1) 62 | 63 | self.scheduler.schedule(action) 64 | return self.subscription 65 | -------------------------------------------------------------------------------- /rx/backpressure/windowed.py: -------------------------------------------------------------------------------- 1 | from rx.internal import extensionmethod 2 | 3 | from .controlledobservable import ControlledObservable 4 | from .windowedobservable import WindowedObservable 5 | 6 | 7 | @extensionmethod(ControlledObservable) 8 | def windowed(self, window_size): 9 | """Creates a sliding windowed observable based upon the window size. 10 | 11 | Keyword arguments: 12 | :param int window_size: The number of items in the window 13 | 14 | :returns: A windowed observable based upon the window size. 15 | :rtype: Observable 16 | """ 17 | 18 | return WindowedObservable(self, window_size) -------------------------------------------------------------------------------- /rx/concurrency/__init__.py: -------------------------------------------------------------------------------- 1 | from .scheduleditem import ScheduledItem 2 | 3 | from .immediatescheduler import ImmediateScheduler, immediate_scheduler 4 | from .currentthreadscheduler import CurrentThreadScheduler, \ 5 | current_thread_scheduler 6 | from .virtualtimescheduler import VirtualTimeScheduler 7 | from .timeoutscheduler import TimeoutScheduler, timeout_scheduler 8 | from .newthreadscheduler import NewThreadScheduler, new_thread_scheduler 9 | try: 10 | from .threadpoolscheduler import ThreadPoolScheduler, thread_pool_scheduler 11 | except ImportError: 12 | pass 13 | from .eventloopscheduler import EventLoopScheduler 14 | from .historicalscheduler import HistoricalScheduler 15 | from .catchscheduler import CatchScheduler 16 | 17 | from .mainloopscheduler import AsyncIOScheduler 18 | from .mainloopscheduler import IOLoopScheduler 19 | from .mainloopscheduler import GEventScheduler 20 | from .mainloopscheduler import GtkScheduler 21 | from .mainloopscheduler import TwistedScheduler 22 | from .mainloopscheduler import TkinterScheduler 23 | from .mainloopscheduler import PyGameScheduler 24 | from .mainloopscheduler import QtScheduler 25 | from .mainloopscheduler import WxScheduler 26 | from .mainloopscheduler import EventLetEventScheduler 27 | -------------------------------------------------------------------------------- /rx/concurrency/historicalscheduler.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from .virtualtimescheduler import VirtualTimeScheduler 3 | 4 | 5 | class HistoricalScheduler(VirtualTimeScheduler): 6 | """Provides a virtual time scheduler that uses datetime for absolute time 7 | and timedelta for relative time.""" 8 | 9 | def __init__(self, initial_clock=None, comparer=None): 10 | """Creates a new historical scheduler with the specified initial clock 11 | value. 12 | 13 | Keyword arguments: 14 | initial_clock -- {Number} Initial value for the clock. 15 | comparer -- {Function} Comparer to determine causality of events based 16 | on absolute time.""" 17 | 18 | def compare_datetimes(a, b): 19 | return (a > b) - (a < b) 20 | 21 | clock = initial_clock or datetime.fromtimestamp(0) 22 | comparer = comparer or compare_datetimes 23 | 24 | super(HistoricalScheduler, self).__init__(clock) 25 | 26 | @property 27 | def now(self): 28 | """Represents a notion of time for this scheduler. Tasks being scheduled 29 | on a scheduler will adhere to the time denoted by this property.""" 30 | 31 | return self.clock 32 | 33 | @staticmethod 34 | def add(absolute, relative): 35 | """Adds a relative time value to an absolute time value. 36 | 37 | Keyword arguments: 38 | absolute -- {datetime} Absolute virtual time value. 39 | relative -- {timedelta} Relative virtual time value to add. 40 | 41 | Returns resulting absolute virtual time sum value.""" 42 | 43 | return absolute + relative 44 | 45 | def to_datetime_offset(self, absolute): 46 | """Converts the absolute time value to a datetime value.""" 47 | 48 | # datetime -> datetime 49 | return absolute 50 | 51 | def to_relative(self, timespan): 52 | """Converts the timespan value to a relative virtual time value. 53 | 54 | Keyword arguments: 55 | timespan -- {timedelta} Time_span value to convert. 56 | 57 | Returns corresponding relative virtual time value.""" 58 | 59 | # timedelta -> timedelta 60 | return timespan 61 | -------------------------------------------------------------------------------- /rx/concurrency/immediatescheduler.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | 3 | from rx.core import Scheduler 4 | from rx.internal.exceptions import WouldBlockException 5 | from .schedulerbase import SchedulerBase 6 | 7 | 8 | class ImmediateScheduler(SchedulerBase): 9 | def schedule(self, action, state=None): 10 | """Schedules an action to be executed.""" 11 | 12 | return self.invoke_action(action, state) 13 | 14 | def schedule_relative(self, duetime, action, state=None): 15 | """Schedules an action to be executed after duetime.""" 16 | 17 | duetime = self.to_timedelta(duetime) 18 | if duetime > timedelta(0): 19 | raise WouldBlockException() 20 | 21 | return self.invoke_action(action, state) 22 | 23 | def schedule_absolute(self, duetime, action, state=None): 24 | """Schedules an action to be executed at duetime.""" 25 | 26 | duetime = self.to_datetime(duetime) 27 | return self.schedule_relative(duetime - self.now, action, state) 28 | 29 | 30 | Scheduler.immediate = immediate_scheduler = ImmediateScheduler() 31 | -------------------------------------------------------------------------------- /rx/concurrency/mainloopscheduler/__init__.py: -------------------------------------------------------------------------------- 1 | from .asyncioscheduler import AsyncIOScheduler 2 | from .ioloopscheduler import IOLoopScheduler 3 | from .geventscheduler import GEventScheduler 4 | from .gtkscheduler import GtkScheduler 5 | from .twistedscheduler import TwistedScheduler 6 | from .tkinterscheduler import TkinterScheduler 7 | from .pygamescheduler import PyGameScheduler 8 | from .qtscheduler import QtScheduler 9 | from .wxscheduler import WxScheduler 10 | from .eventletscheduler import EventLetEventScheduler 11 | -------------------------------------------------------------------------------- /rx/concurrency/mainloopscheduler/tkinterscheduler.py: -------------------------------------------------------------------------------- 1 | from rx.core import Disposable 2 | from rx.disposables import SingleAssignmentDisposable, CompositeDisposable 3 | from rx.concurrency.schedulerbase import SchedulerBase 4 | 5 | 6 | class TkinterScheduler(SchedulerBase): 7 | """A scheduler that schedules work via the Tkinter main event loop. 8 | 9 | http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html 10 | http://effbot.org/tkinterbook/widget.htm""" 11 | 12 | def __init__(self, master): 13 | self.master = master 14 | 15 | def schedule(self, action, state=None): 16 | """Schedules an action to be executed.""" 17 | 18 | return self.schedule_relative(0, action, state) 19 | 20 | def schedule_relative(self, duetime, action, state=None): 21 | """Schedules an action to be executed after duetime. 22 | 23 | Keyword arguments: 24 | duetime -- {timedelta} Relative time after which to execute the action. 25 | action -- {Function} Action to be executed. 26 | 27 | Returns {Disposable} The disposable object used to cancel the scheduled 28 | action (best effort).""" 29 | 30 | msecs = self.to_relative(duetime) 31 | 32 | disposable = SingleAssignmentDisposable() 33 | 34 | def invoke_action(): 35 | disposable.disposable = self.invoke_action(action, state) 36 | 37 | alarm = self.master.after(msecs, invoke_action) 38 | 39 | def dispose(): 40 | self.master.after_cancel(alarm) 41 | 42 | return CompositeDisposable(disposable, Disposable.create(dispose)) 43 | 44 | def schedule_absolute(self, duetime, action, state=None): 45 | """Schedules an action to be executed at duetime. 46 | 47 | Keyword arguments: 48 | duetime -- {datetime} Absolute time after which to execute the action. 49 | action -- {Function} Action to be executed. 50 | 51 | Returns {Disposable} The disposable object used to cancel the scheduled 52 | action (best effort).""" 53 | 54 | duetime = self.to_datetime(duetime) 55 | return self.schedule_relative(duetime - self.now, action, state) 56 | -------------------------------------------------------------------------------- /rx/concurrency/scheduleditem.py: -------------------------------------------------------------------------------- 1 | from rx.disposables import SingleAssignmentDisposable 2 | 3 | 4 | def default_sub_comparer(x, y): 5 | return 0 if x == y else 1 if x > y else -1 6 | 7 | 8 | class ScheduledItem(object): 9 | def __init__(self, scheduler, state, action, duetime): 10 | self.scheduler = scheduler 11 | self.state = state 12 | self.action = action 13 | self.duetime = duetime 14 | self.disposable = SingleAssignmentDisposable() 15 | 16 | def invoke(self): 17 | ret = self.scheduler.invoke_action(self.action, self.state) 18 | self.disposable.disposable = ret 19 | 20 | def cancel(self): 21 | """Cancels the work item by disposing the resource returned by 22 | invoke_core as soon as possible.""" 23 | 24 | self.disposable.dispose() 25 | 26 | def is_cancelled(self): 27 | return self.disposable.is_disposed 28 | 29 | def __lt__(self, other): 30 | return self.duetime < other.duetime 31 | 32 | def __gt__(self, other): 33 | return self.duetime > other.duetime 34 | 35 | def __eq__(self, other): 36 | return self.duetime == other.duetime 37 | -------------------------------------------------------------------------------- /rx/concurrency/scheduleperiodic.py: -------------------------------------------------------------------------------- 1 | from rx.disposables import SerialDisposable 2 | 3 | 4 | class SchedulePeriodic(object): 5 | """Scheduler with support for running periodic tasks. This type of 6 | scheduler can be used to run timers more efficiently instead of using 7 | recursive scheduling.""" 8 | 9 | def __init__(self, scheduler, period, action, state=None): 10 | """ 11 | Keyword arguments: 12 | state -- Initial state passed to the action upon the first iteration. 13 | period -- Period for running the work periodically. 14 | action -- Action to be executed, potentially updating the state.""" 15 | 16 | self._scheduler = scheduler 17 | self._state = state 18 | self._period = period 19 | self._action = action 20 | self._cancel = SerialDisposable() 21 | 22 | def tick(self, scheduler, command): 23 | self._cancel.disposable = self._scheduler.schedule_relative(self._period, self.tick, 0) 24 | try: 25 | new_state = self._action(self._state) 26 | except Exception: 27 | self._cancel.dispose() 28 | raise 29 | else: 30 | if new_state is not None: # Update state if other than None 31 | self._state = new_state 32 | 33 | def start(self): 34 | """Returns the disposable object used to cancel the scheduled recurring 35 | action (best effort). 36 | """ 37 | 38 | self._cancel.disposable = self._scheduler.schedule_relative(self._period, self.tick, 0) 39 | return self._cancel 40 | -------------------------------------------------------------------------------- /rx/concurrency/threadpoolscheduler.py: -------------------------------------------------------------------------------- 1 | from concurrent.futures import ThreadPoolExecutor 2 | 3 | from rx.core import Scheduler 4 | 5 | from .newthreadscheduler import NewThreadScheduler 6 | 7 | 8 | class ThreadPoolScheduler(NewThreadScheduler): 9 | """A scheduler that schedules work via the thread pool.""" 10 | 11 | class ThreadPoolThread: 12 | """Wraps a concurrent future as a thread.""" 13 | 14 | def __init__(self, executor, run): 15 | self.run = run 16 | self.future = None 17 | self.executor = executor 18 | 19 | def start(self): 20 | self.future = self.executor.submit(self.run) 21 | 22 | def cancel(self): 23 | self.future.cancel() 24 | 25 | def __init__(self, max_workers=None): 26 | super(ThreadPoolScheduler, self).__init__(self.thread_factory) 27 | self.executor = ThreadPoolExecutor(max_workers=max_workers) 28 | 29 | def thread_factory(self, target, *args): 30 | return self.ThreadPoolThread(self.executor, target) 31 | 32 | Scheduler.thread_pool = thread_pool_scheduler = ThreadPoolScheduler() 33 | -------------------------------------------------------------------------------- /rx/core/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | import sys 3 | 4 | if sys.version_info >= (3, 0): 5 | from .py3.observer import Observer 6 | from .py3.observable import Observable 7 | from .py3.disposable import Disposable 8 | from .py3.scheduler import Scheduler 9 | else: 10 | from .py2.observer import Observer 11 | from .py2.observable import Observable 12 | from .py2.disposable import Disposable 13 | from .py2.scheduler import Scheduler 14 | 15 | from .observerbase import ObserverBase 16 | from .observablebase import ObservableBase 17 | from .anonymousobserver import AnonymousObserver 18 | from .anonymousobservable import AnonymousObservable 19 | 20 | from . import checkedobserver 21 | from . import observerextensions 22 | from . import disposableextensions 23 | -------------------------------------------------------------------------------- /rx/core/anonymousobservable.py: -------------------------------------------------------------------------------- 1 | from .observablebase import ObservableBase 2 | 3 | 4 | class AnonymousObservable(ObservableBase): 5 | """Class to create an Observable instance from a delegate-based 6 | implementation of the Subscribe method.""" 7 | 8 | def __init__(self, subscribe): 9 | """Creates an observable sequence object from the specified 10 | subscription function. 11 | 12 | Keyword arguments: 13 | :param types.FunctionType subscribe: Subscribe method implementation. 14 | """ 15 | 16 | self._subscribe = subscribe 17 | super(AnonymousObservable, self).__init__() 18 | 19 | def _subscribe_core(self, observer): 20 | return self._subscribe(observer) 21 | -------------------------------------------------------------------------------- /rx/core/anonymousobserver.py: -------------------------------------------------------------------------------- 1 | from rx.internal import noop, default_error 2 | from .observerbase import ObserverBase 3 | 4 | 5 | class AnonymousObserver(ObserverBase): 6 | def __init__(self, on_next=None, on_error=None, on_completed=None): 7 | super(AnonymousObserver, self).__init__() 8 | 9 | self._next = on_next or noop 10 | self._error = on_error or default_error 11 | self._completed = on_completed or noop 12 | 13 | def _on_next_core(self, value): 14 | self._next(value) 15 | 16 | def _on_error_core(self, error): 17 | self._error(error) 18 | 19 | def _on_completed_core(self): 20 | self._completed() 21 | -------------------------------------------------------------------------------- /rx/core/autodetachobserver.py: -------------------------------------------------------------------------------- 1 | from rx.disposables import SingleAssignmentDisposable 2 | 3 | from .observerbase import ObserverBase 4 | 5 | 6 | class AutoDetachObserver(ObserverBase): 7 | 8 | def __init__(self, observer): 9 | super(AutoDetachObserver, self).__init__() 10 | 11 | self.observer = observer 12 | self.m = SingleAssignmentDisposable() 13 | 14 | def _on_next_core(self, value): 15 | try: 16 | self.observer.on_next(value) 17 | except Exception: 18 | self.dispose() 19 | raise 20 | 21 | def _on_error_core(self, exn): 22 | try: 23 | self.observer.on_error(exn) 24 | finally: 25 | self.dispose() 26 | 27 | def _on_completed_core(self): 28 | try: 29 | self.observer.on_completed() 30 | finally: 31 | self.dispose() 32 | 33 | def set_disposable(self, value): 34 | self.m.disposable = value 35 | 36 | disposable = property(fset=set_disposable) 37 | 38 | def dispose(self): 39 | super(AutoDetachObserver, self).dispose() 40 | self.m.dispose() 41 | -------------------------------------------------------------------------------- /rx/core/blockingobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import ObservableBase 2 | 3 | 4 | class BlockingObservable(ObservableBase): 5 | def __init__(self, observable=None): 6 | """Turns an observable into a blocking observable. 7 | 8 | Keyword arguments: 9 | :param Observable observable: Observable to make blocking. 10 | 11 | :returns: Blocking observable 12 | :rtype: BlockingObservable 13 | """ 14 | 15 | self.observable = observable 16 | super(BlockingObservable, self).__init__() 17 | 18 | def _subscribe_core(self, observer): 19 | return self.observable.subscribe(observer) 20 | -------------------------------------------------------------------------------- /rx/core/checkedobserver.py: -------------------------------------------------------------------------------- 1 | from . import Observer 2 | from rx.internal import extensionmethod 3 | from rx.internal.exceptions import ReEntracyException, CompletedException 4 | 5 | 6 | class CheckedObserver(Observer): 7 | 8 | def __init__(self, observer): 9 | self._observer = observer 10 | self._state = 0 # 0 - idle, 1 - busy, 2 - done 11 | 12 | def on_next(self, value): 13 | self.check_access() 14 | try: 15 | self._observer.on_next(value) 16 | finally: 17 | self._state = 0 18 | 19 | def on_error(self, err): 20 | self.check_access() 21 | try: 22 | self._observer.on_error(err) 23 | finally: 24 | self._state = 2 25 | 26 | def on_completed(self): 27 | self.check_access() 28 | try: 29 | self._observer.on_completed() 30 | finally: 31 | self._state = 2 32 | 33 | def check_access(self): 34 | """Checks access to the observer for grammar violations. 35 | 36 | OnNext* (OnError | OnCompleted)? 37 | """ 38 | 39 | if self._state == 1: 40 | raise ReEntracyException() 41 | if self._state == 2: 42 | raise CompletedException() 43 | if self._state == 0: 44 | self._state = 1 45 | 46 | 47 | @extensionmethod(Observer) 48 | def checked(self): 49 | """Checks access to the observer for grammar violations. This includes 50 | checking for multiple OnError or OnCompleted calls, as well as 51 | reentrancy in any of the observer methods. If a violation is detected, 52 | an Error is thrown from the offending observer method call. 53 | 54 | Returns an observer that checks callbacks invocations against the 55 | observer grammar and, if the checks pass, forwards those to the 56 | specified observer.""" 57 | 58 | return CheckedObserver(self) 59 | -------------------------------------------------------------------------------- /rx/core/disposableextensions.py: -------------------------------------------------------------------------------- 1 | from rx.core import Disposable 2 | from rx.internal import noop, extensionclassmethod 3 | from rx.disposables import AnonymousDisposable 4 | 5 | 6 | @extensionclassmethod(Disposable) 7 | def empty(cls): 8 | return AnonymousDisposable(noop) 9 | 10 | 11 | @extensionclassmethod(Disposable) 12 | def create(cls, action): 13 | return AnonymousDisposable(action) 14 | -------------------------------------------------------------------------------- /rx/core/observeonobserver.py: -------------------------------------------------------------------------------- 1 | from rx.core.scheduledobserver import ScheduledObserver 2 | 3 | 4 | class ObserveOnObserver(ScheduledObserver): 5 | def __init__(self, scheduler, observer): 6 | super(ObserveOnObserver, self).__init__(scheduler, observer) 7 | 8 | def _on_next_core(self, value): 9 | super(ObserveOnObserver, self)._on_next_core(value) 10 | self.ensure_active() 11 | 12 | def _on_error_core(self, e): 13 | super(ObserveOnObserver, self)._on_error_core(e) 14 | self.ensure_active() 15 | 16 | def _on_completed_core(self): 17 | super(ObserveOnObserver, self)._on_completed_core() 18 | self.ensure_active() 19 | -------------------------------------------------------------------------------- /rx/core/observerbase.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | 3 | from rx.internal import noop 4 | from . import Observer, Disposable 5 | 6 | 7 | class ObserverBase(Observer, Disposable): 8 | """Base class for implementations of the Observer class. This base 9 | class enforces the grammar of observers where OnError and 10 | OnCompleted are terminal messages. 11 | """ 12 | 13 | def __init__(self): 14 | self.is_stopped = False 15 | 16 | def on_next(self, value): 17 | """Notify the observer of a new element in the sequence.""" 18 | if not self.is_stopped: 19 | self._on_next_core(value) 20 | 21 | @abstractmethod 22 | def _on_next_core(self, value): 23 | return NotImplemented 24 | 25 | def on_error(self, error): 26 | """Notifies the observer that an exception has occurred. 27 | 28 | Keyword arguments: 29 | error -- The error that has occurred.""" 30 | 31 | if not self.is_stopped: 32 | ObserverBase.dispose(self) 33 | self._on_error_core(error) 34 | 35 | @abstractmethod 36 | def _on_error_core(self, error): 37 | return NotImplemented 38 | 39 | def on_completed(self): 40 | """Notifies the observer of the end of the sequence.""" 41 | 42 | if not self.is_stopped: 43 | ObserverBase.dispose(self) 44 | self._on_completed_core() 45 | 46 | @abstractmethod 47 | def _on_completed_core(self): 48 | return NotImplemented 49 | 50 | def dispose(self): 51 | """Disposes the observer, causing it to transition to the stopped 52 | state.""" 53 | 54 | self.on_next = noop 55 | self.is_stopped = True 56 | 57 | def fail(self, exn): 58 | if not self.is_stopped: 59 | ObserverBase.dispose(self) 60 | self._on_error_core(exn) 61 | return True 62 | 63 | return False 64 | -------------------------------------------------------------------------------- /rx/core/observerextensions.py: -------------------------------------------------------------------------------- 1 | from rx.internal import extensionmethod 2 | 3 | from . import Observer 4 | from .anonymousobserver import AnonymousObserver 5 | 6 | 7 | @extensionmethod(Observer) 8 | def to_notifier(self): 9 | """Creates a notification callback from an observer. 10 | 11 | Returns the action that forwards its input notification to the 12 | underlying observer.""" 13 | 14 | observer = self 15 | 16 | def func(notifier): 17 | return notifier.accept(observer) 18 | return func 19 | 20 | 21 | @extensionmethod(Observer) 22 | def as_observer(self): 23 | """Hides the identity of an observer. 24 | 25 | Returns an observer that hides the identity of the specified observer. 26 | """ 27 | 28 | return AnonymousObserver(self.on_next, self.on_error, self.on_completed) 29 | -------------------------------------------------------------------------------- /rx/core/py2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/rx/core/py2/__init__.py -------------------------------------------------------------------------------- /rx/core/py2/disposable.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Disposable(object): 5 | """Abstract disposable class""" 6 | 7 | __metaclass__ = ABCMeta 8 | 9 | @abstractmethod 10 | def dispose(self): 11 | return NotImplemented 12 | 13 | def __enter__(self): 14 | """Context management protocol.""" 15 | pass 16 | 17 | def __exit__(self, type, value, traceback): 18 | """Context management protocol.""" 19 | self.dispose() 20 | -------------------------------------------------------------------------------- /rx/core/py2/observable.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Observable(object): 5 | __metaclass__ = ABCMeta 6 | 7 | @abstractmethod 8 | def subscribe(self, observer): 9 | return NotImplemented 10 | -------------------------------------------------------------------------------- /rx/core/py2/observer.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Observer(object): 5 | __metaclass__ = ABCMeta 6 | 7 | @abstractmethod 8 | def on_next(self, value): 9 | return NotImplemented 10 | 11 | @abstractmethod 12 | def on_error(self, error): 13 | return NotImplemented 14 | 15 | @abstractmethod 16 | def on_completed(self): 17 | return NotImplemented 18 | -------------------------------------------------------------------------------- /rx/core/py2/scheduler.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Scheduler(object): 5 | __metaclass__ = ABCMeta 6 | 7 | @property 8 | @abstractmethod 9 | def now(self): 10 | return NotImplemented 11 | 12 | @abstractmethod 13 | def schedule(self, action, state=None): 14 | return NotImplemented 15 | 16 | @abstractmethod 17 | def schedule_relative(self, duetime, action, state=None): 18 | return NotImplemented 19 | 20 | @abstractmethod 21 | def schedule_absolute(self, duetime, action, state=None): 22 | return NotImplemented 23 | -------------------------------------------------------------------------------- /rx/core/py3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/rx/core/py3/__init__.py -------------------------------------------------------------------------------- /rx/core/py3/disposable.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Disposable(metaclass=ABCMeta): 5 | """Abstract disposable class""" 6 | 7 | @abstractmethod 8 | def dispose(self): 9 | return NotImplemented 10 | 11 | def __enter__(self): 12 | """Context management protocol.""" 13 | pass 14 | 15 | def __exit__(self, type, value, traceback): 16 | """Context management protocol.""" 17 | self.dispose() 18 | -------------------------------------------------------------------------------- /rx/core/py3/observable.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Observable(metaclass=ABCMeta): 5 | @abstractmethod 6 | def subscribe(self, observer): 7 | return NotImplemented 8 | -------------------------------------------------------------------------------- /rx/core/py3/observer.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Observer(metaclass=ABCMeta): 5 | @abstractmethod 6 | def on_next(self, value): 7 | return NotImplemented 8 | 9 | @abstractmethod 10 | def on_error(self, error): 11 | return NotImplemented 12 | 13 | @abstractmethod 14 | def on_completed(self): 15 | return NotImplemented 16 | -------------------------------------------------------------------------------- /rx/core/py3/scheduler.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Scheduler(metaclass=ABCMeta): 5 | @property 6 | @abstractmethod 7 | def now(self): 8 | return NotImplemented 9 | 10 | @abstractmethod 11 | def schedule(self, action, state=None): 12 | return NotImplemented 13 | 14 | @abstractmethod 15 | def schedule_relative(self, duetime, action, state=None): 16 | return NotImplemented 17 | 18 | @abstractmethod 19 | def schedule_absolute(self, duetime, action, state=None): 20 | return NotImplemented 21 | -------------------------------------------------------------------------------- /rx/disposables/__init__.py: -------------------------------------------------------------------------------- 1 | from .anonymousdisposable import AnonymousDisposable 2 | from .booleandisposable import BooleanDisposable 3 | from .compositedisposable import CompositeDisposable 4 | from .singleassignmentdisposable import SingleAssignmentDisposable 5 | from .multipleassignmentdisposable import MultipleAssignmentDisposable 6 | from .serialdisposable import SerialDisposable 7 | from .refcountdisposable import RefCountDisposable 8 | from .scheduleddisposable import ScheduledDisposable 9 | -------------------------------------------------------------------------------- /rx/disposables/anonymousdisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.internal import noop 3 | from rx.core import Disposable 4 | 5 | 6 | class AnonymousDisposable(Disposable): 7 | """Main disposable class""" 8 | 9 | def __init__(self, action=None): 10 | """Creates a disposable object that invokes the specified action when 11 | disposed. 12 | 13 | Keyword arguments: 14 | dispose -- Action to run during the first call to Disposable.dispose. 15 | The action is guaranteed to be run at most once. 16 | 17 | Returns the disposable object that runs the given action upon disposal. 18 | """ 19 | 20 | self.is_disposed = False 21 | self.action = action or noop 22 | 23 | self.lock = config["concurrency"].RLock() 24 | 25 | def dispose(self): 26 | """Performs the task of cleaning up resources.""" 27 | 28 | dispose = False 29 | with self.lock: 30 | if not self.is_disposed: 31 | dispose = True 32 | self.is_disposed = True 33 | 34 | if dispose: 35 | self.action() 36 | 37 | @classmethod 38 | def empty(cls): 39 | return cls(noop) 40 | 41 | @classmethod 42 | def create(cls, action): 43 | return cls(action) 44 | -------------------------------------------------------------------------------- /rx/disposables/booleandisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Disposable 3 | 4 | 5 | class BooleanDisposable(Disposable): 6 | """Represents a Disposable that can be checked for status.""" 7 | 8 | def __init__(self): 9 | """Initializes a new instance of the BooleanDisposable class.""" 10 | 11 | self.is_disposed = False 12 | self.lock = config["concurrency"].RLock() 13 | 14 | super(BooleanDisposable, self).__init__() 15 | 16 | def dispose(self): 17 | """Sets the status to disposed""" 18 | 19 | self.is_disposed = True 20 | -------------------------------------------------------------------------------- /rx/disposables/multipleassignmentdisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Disposable 3 | 4 | 5 | class MultipleAssignmentDisposable(Disposable): 6 | """Represents a disposable resource whose underlying disposable 7 | resource can be replaced by another disposable resource.""" 8 | 9 | def __init__(self): 10 | self.current = None 11 | self.is_disposed = False 12 | self.lock = config["concurrency"].RLock() 13 | 14 | super(MultipleAssignmentDisposable, self).__init__() 15 | 16 | def get_disposable(self): 17 | return self.current 18 | 19 | def set_disposable(self, value): 20 | """If the MultipleAssignmentDisposable has already been 21 | disposed, assignment to this property causes immediate disposal 22 | of the given disposable object.""" 23 | 24 | should_dispose = self.is_disposed 25 | 26 | with self.lock: 27 | if not should_dispose: 28 | self.current = value 29 | 30 | if should_dispose and value: 31 | value.dispose() 32 | 33 | disposable = property(get_disposable, set_disposable) 34 | 35 | def dispose(self): 36 | """Disposes the underlying disposable as well as all future 37 | replacements.""" 38 | 39 | old = None 40 | 41 | with self.lock: 42 | if not self.is_disposed: 43 | self.is_disposed = True 44 | old = self.current 45 | self.current = None 46 | 47 | if old: 48 | old.dispose() 49 | -------------------------------------------------------------------------------- /rx/disposables/scheduleddisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Disposable 3 | 4 | 5 | class ScheduledDisposable(Disposable): 6 | """Represents a disposable resource whose disposal invocation will be 7 | scheduled on the specified Scheduler""" 8 | 9 | def __init__(self, scheduler, disposable): 10 | """Initializes a new instance of the ScheduledDisposable class that 11 | uses a Scheduler on which to dispose the disposable.""" 12 | 13 | self.scheduler = scheduler 14 | self.disposable = disposable 15 | self.is_disposed = False 16 | self.lock = config["concurrency"].RLock() 17 | 18 | super(ScheduledDisposable, self).__init__() 19 | 20 | def dispose(self): 21 | """Disposes the wrapped disposable on the provided scheduler.""" 22 | 23 | parent = self 24 | 25 | def action(scheduler, state): 26 | """Scheduled dispose action""" 27 | 28 | should_dispose = False 29 | 30 | with self.lock: 31 | if not parent.is_disposed: 32 | parent.is_disposed = True 33 | should_dispose = True 34 | if should_dispose: 35 | parent.disposable.dispose() 36 | 37 | self.scheduler.schedule(action) 38 | -------------------------------------------------------------------------------- /rx/disposables/serialdisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Disposable 3 | 4 | 5 | class SerialDisposable(Disposable): 6 | """Represents a disposable resource whose underlying disposable resource can 7 | be replaced by another disposable resource, causing automatic disposal of 8 | the previous underlying disposable resource.""" 9 | 10 | def __init__(self): 11 | self.current = None 12 | self.is_disposed = False 13 | self.lock = config["concurrency"].RLock() 14 | 15 | super(SerialDisposable, self).__init__() 16 | 17 | def get_disposable(self): 18 | return self.current 19 | 20 | def set_disposable(self, value): 21 | """If the SerialDisposable has already been disposed, assignment to this 22 | property causes immediate disposal of the given disposable object. 23 | Assigning this property disposes the previous disposable object.""" 24 | 25 | should_dispose = self.is_disposed 26 | old = None 27 | 28 | with self.lock: 29 | if not should_dispose: 30 | old = self.current 31 | self.current = value 32 | 33 | if old: 34 | old.dispose() 35 | 36 | if should_dispose and value: 37 | value.dispose() 38 | 39 | disposable = property(get_disposable, set_disposable) 40 | 41 | def dispose(self): 42 | """Disposes the underlying disposable as well as all future 43 | replacements.""" 44 | 45 | old = None 46 | 47 | with self.lock: 48 | if not self.is_disposed: 49 | self.is_disposed = True 50 | old = self.current 51 | self.current = None 52 | 53 | if old: 54 | old.dispose() 55 | -------------------------------------------------------------------------------- /rx/disposables/singleassignmentdisposable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Disposable 3 | 4 | 5 | class SingleAssignmentDisposable(Disposable): 6 | """Represents a disposable resource which only allows a single assignment 7 | of its underlying disposable resource. If an underlying disposable resource 8 | has already been set, future attempts to set the underlying disposable 9 | resource will throw an Error.""" 10 | 11 | def __init__(self): 12 | """Initializes a new instance of the SingleAssignmentDisposable 13 | class. 14 | """ 15 | self.is_disposed = False 16 | self.current = None 17 | self.lock = config["concurrency"].RLock() 18 | 19 | super(Disposable, self).__init__() 20 | 21 | def get_disposable(self): 22 | return self.current 23 | 24 | def set_disposable(self, value): 25 | if self.current: 26 | raise Exception('Disposable has already been assigned') 27 | 28 | should_dispose = self.is_disposed 29 | old = None 30 | 31 | with self.lock: 32 | if not should_dispose: 33 | old = self.current 34 | self.current = value 35 | 36 | if old: 37 | old.dispose() 38 | 39 | if should_dispose and value: 40 | value.dispose() 41 | 42 | disposable = property(get_disposable, set_disposable) 43 | 44 | def dispose(self): 45 | """Sets the status to disposed""" 46 | old = None 47 | 48 | with self.lock: 49 | if not self.is_disposed: 50 | self.is_disposed = True 51 | old = self.current 52 | self.current = None 53 | 54 | if old: 55 | old.dispose() 56 | -------------------------------------------------------------------------------- /rx/internal/__init__.py: -------------------------------------------------------------------------------- 1 | from .priorityqueue import PriorityQueue 2 | from .basic import noop, default_error, default_comparer 3 | from .exceptions import SequenceContainsNoElementsError, ArgumentOutOfRangeException, DisposedException 4 | from .extensionmethod import extensionmethod, extensionclassmethod 5 | from .enumerable import Enumerable 6 | from .enumerator import Enumerator 7 | from . import concurrency 8 | -------------------------------------------------------------------------------- /rx/internal/basic.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | # Defaults 5 | def noop(*args, **kw): 6 | """No operation. Returns nothing""" 7 | pass 8 | 9 | 10 | def identity(x): 11 | """Returns argument x""" 12 | return x 13 | 14 | 15 | def default_now(): 16 | return datetime.utcnow() 17 | 18 | 19 | def default_comparer(x, y): 20 | return x == y 21 | 22 | 23 | def default_sub_comparer(x, y): 24 | return x - y 25 | 26 | 27 | def default_key_serializer(x): 28 | return str(x) 29 | 30 | 31 | def default_error(err): 32 | if isinstance(err, BaseException): 33 | raise err 34 | else: 35 | raise Exception(err) 36 | -------------------------------------------------------------------------------- /rx/internal/concurrency.py: -------------------------------------------------------------------------------- 1 | 2 | class RLock(object): 3 | """Dummy lock object for schedulers that don't need locking""" 4 | 5 | def locked(self): 6 | return False 7 | 8 | def __enter__(self): 9 | """Context management protocol""" 10 | pass 11 | 12 | def __exit__(self, type, value, traceback): 13 | """Context management protocol""" 14 | pass 15 | 16 | 17 | class Event(object): 18 | def is_set(self): 19 | return False 20 | 21 | def set(self): 22 | raise NotImplementedError 23 | 24 | def clear(self): 25 | raise NotImplementedError 26 | 27 | def wait(self): 28 | raise NotImplementedError 29 | 30 | 31 | class Condition(object): 32 | def wait(self): 33 | raise NotImplementedError 34 | 35 | def aquire(self): 36 | raise NotImplementedError 37 | 38 | def release(self): 39 | raise NotImplementedError 40 | 41 | def notify(self, n=1): 42 | raise NotImplementedError 43 | 44 | def notify_all(self): 45 | raise NotImplementedError 46 | -------------------------------------------------------------------------------- /rx/internal/enumerable.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | from .basic import identity 4 | 5 | 6 | class Enumerable(object): 7 | 8 | def __init__(self, iterator): 9 | self._iterator = iterator 10 | 11 | def __iter__(self): 12 | return self._iterator 13 | 14 | def where(self, predicate): 15 | return Enumerable(value for value in self if predicate(value)) 16 | 17 | def select(self, selector=None): 18 | selector = selector or identity 19 | return Enumerable(selector(value) for value in self) 20 | 21 | def take(self, count): 22 | def next(): 23 | n = count 24 | 25 | for value in self: 26 | if n <= 0: 27 | raise StopIteration 28 | n -= 1 29 | yield value 30 | 31 | raise StopIteration 32 | return Enumerable(next()) 33 | 34 | @classmethod 35 | def range(cls, start, count): 36 | def next(): 37 | value = start 38 | n = count 39 | while n > 0: 40 | yield value 41 | value += 1 42 | n -= 1 43 | 44 | raise StopIteration 45 | return Enumerable(next()) 46 | 47 | @classmethod 48 | def repeat(cls, value, count=None): 49 | if count is not None: 50 | return Enumerable(value for _ in range(count)) 51 | return Enumerable(itertools.repeat(value)) 52 | 53 | @classmethod 54 | def for_each(cls, source, selector=None): 55 | selector = selector or identity 56 | return Enumerable(selector(value) for value in source) 57 | -------------------------------------------------------------------------------- /rx/internal/enumerator.py: -------------------------------------------------------------------------------- 1 | class Enumerator(object): 2 | """For Python we just wrap the iterator""" 3 | 4 | def __init__(self, next): 5 | self.iterator = next 6 | 7 | def __next__(self): 8 | return next(self.iterator) 9 | 10 | # Python 2.7 11 | next = __next__ 12 | 13 | def __iter__(self): 14 | return self 15 | -------------------------------------------------------------------------------- /rx/internal/exceptions.py: -------------------------------------------------------------------------------- 1 | # Rx Exceptions 2 | 3 | 4 | class SequenceContainsNoElementsError(Exception): 5 | def __init__(self, msg=None): 6 | super(SequenceContainsNoElementsError, self).__init__(msg or "Sequence contains no elements") 7 | 8 | 9 | class ArgumentOutOfRangeException(ValueError): 10 | def __init__(self, msg=None): 11 | super(ArgumentOutOfRangeException, self).__init__(msg or "Argument out of range") 12 | 13 | 14 | class DisposedException(Exception): 15 | def __init__(self, msg=None): 16 | super(DisposedException, self).__init__(msg or "Object has been disposed") 17 | 18 | 19 | class ReEntracyException(Exception): 20 | def __init__(self, msg=None): 21 | super(ReEntracyException, self).__init__(msg or 'Re-entrancy detected') 22 | 23 | 24 | class CompletedException(Exception): 25 | def __init__(self, msg=None): 26 | super(CompletedException, self).__init__(msg or 'Observer completed') 27 | 28 | 29 | class WouldBlockException(Exception): 30 | def __init__(self, msg=None): 31 | super(WouldBlockException, self).__init__(msg or "Would block") 32 | -------------------------------------------------------------------------------- /rx/internal/extensionmethod.py: -------------------------------------------------------------------------------- 1 | def extensionmethod(base, name=None, decorator=None, instancemethod=False, alias=None): 2 | """Function decorator that extends base with the decorated 3 | function. 4 | 5 | Keyword arguments: 6 | :param T base: Base class to extend with method 7 | :param string name: Name of method to set 8 | :param Callable decorator: Additional decorator e.g staticmethod 9 | 10 | :returns: A function that takes the class to be decorated. 11 | :rtype: func -> func 12 | """ 13 | 14 | def inner(func): 15 | """This function is returned by the outer extensionmethod() 16 | 17 | :param types.FunctionType func: Function to be decorated 18 | """ 19 | 20 | func_names = [name or func.__name__] 21 | if alias: 22 | aliases = alias if isinstance(alias, list) else [alias] 23 | func_names += aliases 24 | 25 | func = decorator(func) if decorator else func 26 | 27 | for func_name in func_names: 28 | if instancemethod: 29 | if hasattr(base, "_methods"): 30 | base._methods.append((func_name, func)) 31 | else: 32 | base._methods = [(func_name, func)] 33 | else: 34 | setattr(base, func_name, func) 35 | return func 36 | return inner 37 | 38 | 39 | def extensionclassmethod(base, name=None, alias=None): 40 | """Function decorator that extends base with the decorated 41 | function as a class method. 42 | 43 | Keyword arguments: 44 | :param T base: Base class to extend with classmethod 45 | :param string name: Name of method to set 46 | 47 | :returns: A function that takes the class to be decorated. 48 | :rtype: func -> func 49 | """ 50 | 51 | return extensionmethod(base=base, name=name, decorator=classmethod, alias=alias) 52 | -------------------------------------------------------------------------------- /rx/internal/priorityqueue.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | 3 | import rx 4 | 5 | 6 | class PriorityQueue(object): 7 | """Priority queue for scheduling""" 8 | 9 | def __init__(self, capacity=None): 10 | self.items = [] 11 | self.count = 0 # Monotonic increasing for sort stability 12 | 13 | self.lock = rx.config.get("Lock")() 14 | 15 | def __len__(self): 16 | """Returns length of queue""" 17 | 18 | return len(self.items) 19 | 20 | def peek(self): 21 | """Returns first item in queue without removing it""" 22 | 23 | return self.items[0][0] 24 | 25 | def remove_at(self, index): 26 | """Removes item at given index""" 27 | 28 | with self.lock: 29 | item = self.items.pop(index)[0] 30 | heapq.heapify(self.items) 31 | return item 32 | 33 | def dequeue(self): 34 | """Returns and removes item with lowest priority from queue""" 35 | 36 | with self.lock: 37 | item = heapq.heappop(self.items)[0] 38 | return item 39 | 40 | def enqueue(self, item): 41 | """Adds item to queue""" 42 | 43 | with self.lock: 44 | heapq.heappush(self.items, (item, self.count)) 45 | self.count += 1 46 | 47 | def remove(self, item): 48 | """Remove given item from queue""" 49 | 50 | with self.lock: 51 | for index, _item in enumerate(self.items): 52 | if _item[0] == item: 53 | self.items.pop(index) 54 | heapq.heapify(self.items) 55 | return True 56 | 57 | return False 58 | -------------------------------------------------------------------------------- /rx/internal/utils.py: -------------------------------------------------------------------------------- 1 | from rx import AnonymousObservable 2 | from rx.disposables import CompositeDisposable 3 | 4 | from .exceptions import DisposedException 5 | 6 | 7 | def add_ref(xs, r): 8 | def subscribe(observer): 9 | return CompositeDisposable(r.disposable, xs.subscribe(observer)) 10 | 11 | return AnonymousObservable(subscribe) 12 | 13 | 14 | def adapt_call(func): 15 | """Adapt called func. 16 | 17 | Adapt call from taking n params to only taking 1 or 2 params 18 | """ 19 | cached = [None] 20 | 21 | def func1(arg1, *_): 22 | return func(arg1) 23 | 24 | def func2(arg1, arg2=None, *_): 25 | return func(arg1, arg2) 26 | 27 | def func_wrapped(*args, **kw): 28 | if cached[0]: 29 | return cached[0](*args, **kw) 30 | 31 | for fn in (func1, func2): 32 | try: 33 | ret = fn(*args, **kw) 34 | except TypeError: 35 | continue 36 | else: 37 | cached[0] = fn 38 | return ret 39 | 40 | # Error if we get here. Just call original function to generate a 41 | # meaningful error message 42 | return func(*args, **kw) 43 | return func_wrapped 44 | 45 | 46 | def check_disposed(this): 47 | if this.is_disposed: 48 | raise DisposedException() 49 | 50 | 51 | def is_future(p): 52 | return callable(getattr(p, "add_done_callback", None)) 53 | 54 | 55 | class TimeInterval(object): 56 | 57 | def __init__(self, value, interval): 58 | self.value = value 59 | self.interval = interval 60 | 61 | 62 | class Timestamp(object): 63 | 64 | def __init__(self, value, timestamp): 65 | self.value = value 66 | self.timestamp = timestamp 67 | -------------------------------------------------------------------------------- /rx/joins/__init__.py: -------------------------------------------------------------------------------- 1 | from .pattern import Pattern 2 | from .plan import Plan -------------------------------------------------------------------------------- /rx/joins/activeplan.py: -------------------------------------------------------------------------------- 1 | class ActivePlan(object): 2 | def __init__(self, join_observer_list, on_next, on_completed): 3 | self.join_observer_list = join_observer_list 4 | self.on_next = on_next 5 | self.on_completed = on_completed 6 | self.join_observers = {} 7 | for join_observer in self.join_observer_list: 8 | self.join_observers[join_observer] = join_observer 9 | 10 | def dequeue(self): 11 | for join_observer in self.join_observers.values(): 12 | join_observer.queue.pop(0) 13 | 14 | def match(self): 15 | has_values = True 16 | for join_observer in self.join_observer_list: 17 | if not len(join_observer.queue): 18 | has_values = False 19 | break 20 | 21 | if has_values: 22 | first_values = [] 23 | is_completed = False 24 | for join_observer in self.join_observer_list: 25 | first_values.append(join_observer.queue[0]) 26 | if join_observer.queue[0].kind == 'C': 27 | is_completed = True 28 | 29 | if is_completed: 30 | self.on_completed() 31 | else: 32 | self.dequeue() 33 | values = [] 34 | for value in first_values: 35 | values.append(value.value) 36 | 37 | self.on_next(*values) 38 | -------------------------------------------------------------------------------- /rx/joins/joinobserver.py: -------------------------------------------------------------------------------- 1 | from rx.core import ObserverBase 2 | from rx.disposables import SingleAssignmentDisposable 3 | 4 | 5 | class JoinObserver(ObserverBase): 6 | 7 | def __init__(self, source, on_error): 8 | super(JoinObserver, self).__init__() 9 | 10 | self.source = source 11 | self.on_error = on_error 12 | self.queue = [] 13 | self.active_plans = [] 14 | self.subscription = SingleAssignmentDisposable() 15 | self.is_disposed = False 16 | 17 | def _on_next_core(self, notification): 18 | if not self.is_disposed: 19 | if notification.kind == 'E': 20 | self.on_error(notification.exception) 21 | return 22 | 23 | self.queue.append(notification) 24 | active_plans = self.active_plans[:] 25 | for plan in active_plans: 26 | plan.match() 27 | 28 | def _on_error_core(self, error): 29 | return NotImplemented 30 | 31 | def _on_completed_core(self): 32 | return NotImplemented 33 | 34 | def add_active_plan(self, active_plan): 35 | self.active_plans.append(active_plan) 36 | 37 | def subscribe(self): 38 | self.subscription.disposable = self.source.materialize().subscribe(self) 39 | 40 | def remove_active_plan(self, active_plan): 41 | self.active_plans.remove(active_plan) 42 | if not len(self.active_plans): 43 | self.dispose() 44 | 45 | def dispose(self): 46 | super(JoinObserver, self).dispose() 47 | 48 | if not self.is_disposed: 49 | self.is_disposed = True 50 | self.subscription.dispose() 51 | -------------------------------------------------------------------------------- /rx/joins/pattern.py: -------------------------------------------------------------------------------- 1 | from .plan import Plan 2 | 3 | class Pattern(object): 4 | def __init__(self, patterns): 5 | """Represents a join pattern over observable sequences.""" 6 | 7 | self.patterns = patterns 8 | 9 | def and_(self, other): 10 | """Creates a pattern that matches the current plan matches and when the 11 | specified observable sequences has an available value. 12 | 13 | Keyword arguments: 14 | :param Observable other: Observable sequence to match in addition to the 15 | current pattern. 16 | :returns: Pattern object that matches when all observable sequences in 17 | the pattern have an available value. 18 | :rtype: Pattern 19 | """ 20 | 21 | return Pattern(self.patterns + [other]) 22 | 23 | def __and__(self, other): 24 | """Creates a pattern that matches the current plan matches and when the 25 | specified observable sequences has an available value. 26 | 27 | Keyword arguments: 28 | :param Observable other: Observable sequence to match in addition to the 29 | current pattern. 30 | :returns: Pattern object that matches when all observable sequences in 31 | the pattern have an available value. 32 | :rtype: Pattern 33 | """ 34 | 35 | return self.and_(other) 36 | 37 | def then_do(self, selector): 38 | """Matches when all observable sequences in the pattern (specified using 39 | a chain of and operators) have an available value and projects the 40 | values. 41 | 42 | Keyword arguments: 43 | :param types.FunctionType selector: Selector that will be invoked with 44 | available values from the source sequences, in the same order of the 45 | sequences in the pattern. 46 | :returns: Plan that produces the projected values, to be fed (with other 47 | plans) to the when operator. 48 | :rtype: Plan 49 | """ 50 | 51 | return Plan(self, selector) 52 | -------------------------------------------------------------------------------- /rx/joins/plan.py: -------------------------------------------------------------------------------- 1 | from .activeplan import ActivePlan 2 | from .joinobserver import JoinObserver 3 | 4 | 5 | class Plan(object): 6 | def __init__(self, expression, selector): 7 | self.expression = expression 8 | self.selector = selector 9 | 10 | def activate(self, external_subscriptions, observer, deactivate): 11 | join_observers = [] 12 | for pattern in self.expression.patterns: 13 | join_observers.append(self.plan_create_observer(external_subscriptions, pattern, observer.on_error)) 14 | 15 | def on_next(*args): 16 | try: 17 | result = self.selector(*args) 18 | except Exception as e: 19 | observer.on_error(e) 20 | return 21 | observer.on_next(result) 22 | 23 | def on_completed(): 24 | for join_observer in join_observers: 25 | join_observer.remove_active_plan(active_plan) 26 | 27 | deactivate(active_plan) 28 | 29 | active_plan = ActivePlan(join_observers, on_next, on_completed) 30 | 31 | for join_observer in join_observers: 32 | join_observer.add_active_plan(active_plan) 33 | 34 | return active_plan 35 | 36 | def plan_create_observer(self, external_subscriptions, observable, on_error): 37 | entry = external_subscriptions.get(observable) 38 | if not entry: 39 | observer = JoinObserver(observable, on_error) 40 | external_subscriptions[observable] = observer 41 | return observer 42 | 43 | return entry 44 | -------------------------------------------------------------------------------- /rx/linq/__init__.py: -------------------------------------------------------------------------------- 1 | from . import observable 2 | from . import enumerable 3 | 4 | -------------------------------------------------------------------------------- /rx/linq/enumerable/__init__.py: -------------------------------------------------------------------------------- 1 | from . import whiledo 2 | -------------------------------------------------------------------------------- /rx/linq/enumerable/whiledo.py: -------------------------------------------------------------------------------- 1 | from rx.internal import Enumerable, Enumerator 2 | from rx.internal import extensionclassmethod 3 | 4 | @extensionclassmethod(Enumerable) 5 | def while_do(cls, condition, source): 6 | def next(): 7 | while condition(source): 8 | yield source 9 | 10 | raise StopIteration() 11 | return Enumerable(next()) 12 | 13 | -------------------------------------------------------------------------------- /rx/linq/groupedobservable.py: -------------------------------------------------------------------------------- 1 | from rx import AnonymousObservable 2 | from rx.core import ObservableBase 3 | from rx.disposables import CompositeDisposable 4 | 5 | 6 | class GroupedObservable(ObservableBase): 7 | def __init__(self, key, underlying_observable, merged_disposable=None): 8 | super(GroupedObservable, self).__init__() 9 | self.key = key 10 | 11 | def subscribe(observer): 12 | return CompositeDisposable(merged_disposable.disposable, underlying_observable.subscribe(observer)) 13 | 14 | self.underlying_observable = underlying_observable if not merged_disposable else AnonymousObservable(subscribe) 15 | 16 | def _subscribe_core(self, observer): 17 | return self.underlying_observable.subscribe(observer) 18 | -------------------------------------------------------------------------------- /rx/linq/observable/all.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable, alias="every") 6 | def all(self, predicate): 7 | """Determines whether all elements of an observable sequence satisfy a 8 | condition. 9 | 10 | 1 - res = source.all(lambda value: value.length > 3) 11 | 12 | Keyword arguments: 13 | :param bool predicate: A function to test each element for a condition. 14 | 15 | :returns: An observable sequence containing a single element determining 16 | whether all elements in the source sequence pass the test in the 17 | specified predicate. 18 | """ 19 | 20 | return self.filter(lambda v: not predicate(v)).some().map(lambda b: not b) 21 | -------------------------------------------------------------------------------- /rx/linq/observable/and_.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from rx.joins import Pattern 5 | 6 | 7 | @extensionmethod(Observable) 8 | def and_(self, right): 9 | """Creates a pattern that matches when both observable sequences 10 | have an available value. 11 | 12 | :param Observable right: Observable sequence to match with the 13 | current sequence. 14 | :returns: Pattern object that matches when both observable sequences 15 | have an available value. 16 | """ 17 | 18 | return Pattern([self, right]) 19 | -------------------------------------------------------------------------------- /rx/linq/observable/asobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def as_observable(self): 7 | """Hides the identity of an observable sequence. 8 | 9 | :returns: An observable sequence that hides the identity of the source 10 | sequence. 11 | :rtype: Observable 12 | """ 13 | 14 | source = self 15 | 16 | def subscribe(observer): 17 | return source.subscribe(observer) 18 | 19 | return AnonymousObservable(subscribe) 20 | -------------------------------------------------------------------------------- /rx/linq/observable/average.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | class AverageValue(object): 6 | 7 | def __init__(self, sum, count): 8 | self.sum = sum 9 | self.count = count 10 | 11 | 12 | @extensionmethod(Observable) 13 | def average(self, key_selector=None): 14 | """Computes the average of an observable sequence of values that are in 15 | the sequence or obtained by invoking a transform function on each 16 | element of the input sequence if present. 17 | 18 | Example 19 | res = source.average(); 20 | res = source.average(lambda x: x.value) 21 | 22 | :param Observable self: Observable to average. 23 | :param types.FunctionType key_selector: A transform function to apply to 24 | each element. 25 | 26 | :returns: An observable sequence containing a single element with the 27 | average of the sequence of values. 28 | :rtype: Observable 29 | """ 30 | 31 | if key_selector: 32 | return self.map(key_selector).average() 33 | 34 | def accumulator(prev, cur): 35 | return AverageValue(sum=prev.sum+cur, count=prev.count+1) 36 | 37 | def mapper(s): 38 | if s.count == 0: 39 | raise Exception('The input sequence was empty') 40 | 41 | return s.sum / float(s.count) 42 | 43 | seed = AverageValue(sum=0, count=0) 44 | return self.scan(accumulator, seed).last().map(mapper) 45 | -------------------------------------------------------------------------------- /rx/linq/observable/blocking/__init__.py: -------------------------------------------------------------------------------- 1 | from . import foreach 2 | from . import toiterable 3 | from . import first 4 | from. import last 5 | -------------------------------------------------------------------------------- /rx/linq/observable/blocking/first.py: -------------------------------------------------------------------------------- 1 | from rx.internal import extensionmethod 2 | 3 | from rx.core.blockingobservable import BlockingObservable 4 | 5 | 6 | @extensionmethod(BlockingObservable) 7 | def first(self): 8 | """ 9 | Blocks until the first element emits from a BlockingObservable. 10 | 11 | If no item is emitted when on_completed() is called, an exception is thrown 12 | 13 | Note: This will block even if the underlying Observable is asynchronous. 14 | 15 | :param self: 16 | :return: the first item to be emitted from a BlockingObservable 17 | """ 18 | return next(self.to_iterable()) 19 | 20 | 21 | @extensionmethod(BlockingObservable) 22 | def first_or_default(self, default_value): 23 | """ 24 | Blocks until the first element emits from a BlockingObservable. 25 | 26 | If no item is emitted when on_completed() is called, the provided default_value is returned instead 27 | 28 | Note: This will block even if the underlying Observable is asynchronous. 29 | 30 | :param default_value: 31 | :param self: 32 | :return: the first item to be emitted from a BlockingObservable 33 | """ 34 | return next(self.to_iterable(), default_value) 35 | 36 | 37 | -------------------------------------------------------------------------------- /rx/linq/observable/blocking/foreach.py: -------------------------------------------------------------------------------- 1 | from rx.core.blockingobservable import BlockingObservable 2 | from rx.internal import extensionmethod 3 | from rx.internal.utils import adapt_call 4 | from rx import config 5 | 6 | 7 | @extensionmethod(BlockingObservable) 8 | def for_each(self, action): 9 | """Invokes a method on each item emitted by this BlockingObservable and 10 | blocks until the Observable completes. 11 | 12 | Note: This will block even if the underlying Observable is asynchronous. 13 | 14 | This is similar to Observable#subscribe(subscriber), but it blocks. Because 15 | it blocks it does not need the Subscriber#on_completed() or 16 | Subscriber#on_error(Throwable) methods. If the underlying Observable 17 | terminates with an error, rather than calling `onError`, this method will 18 | throw an exception. 19 | 20 | Keyword arguments: 21 | :param types.FunctionType action: the action to invoke for each item 22 | emitted by the `BlockingObservable`. 23 | :raises Exception: if an error occurs 24 | :returns: None 25 | :rtype: None 26 | """ 27 | 28 | action = adapt_call(action) 29 | latch = config["concurrency"].Event() 30 | exception = [None] 31 | count = [0] 32 | 33 | def on_next(value): 34 | with self.lock: 35 | i = count[0] 36 | count[0] += 1 37 | action(value, i) 38 | 39 | def on_error(err): 40 | # If we receive an on_error event we set the reference on the 41 | # outer thread so we can git it and throw after the latch.wait() 42 | # 43 | # We do this instead of throwing directly since this may be on 44 | # a different thread and the latch is still waiting. 45 | exception[0] = err 46 | latch.set() 47 | 48 | def on_completed(): 49 | latch.set() 50 | 51 | self.observable.subscribe(on_next, on_error, on_completed) 52 | 53 | # Block until the subscription completes and then return 54 | latch.wait() 55 | 56 | if exception[0] is not None: 57 | raise Exception(exception[0]) 58 | -------------------------------------------------------------------------------- /rx/linq/observable/blocking/last.py: -------------------------------------------------------------------------------- 1 | from rx.internal import extensionmethod 2 | 3 | from rx.core.blockingobservable import BlockingObservable 4 | 5 | 6 | @extensionmethod(BlockingObservable) 7 | def last(self): 8 | """ 9 | Blocks until the last element emits from a BlockingObservable. 10 | 11 | If no item is emitted when on_completed() is called, an exception is thrown 12 | 13 | Note: This will block even if the underlying Observable is asynchronous. 14 | 15 | :param self: 16 | :return: the last item to be emitted from a BlockingObservable 17 | """ 18 | last_item = None 19 | is_empty = True 20 | 21 | for item in self.to_iterable(): 22 | is_empty = False 23 | last_item = item 24 | 25 | if is_empty: 26 | raise Exception("No items were emitted") 27 | 28 | return last_item 29 | 30 | 31 | @extensionmethod(BlockingObservable) 32 | def last_or_default(self, default_value): 33 | """ 34 | Blocks until the last element emits from a BlockingObservable. 35 | 36 | If no item is emitted when on_completed() is called, the provided default_value will be returned 37 | 38 | Note: This will block even if the underlying Observable is asynchronous. 39 | 40 | :param default_value: 41 | :param self: 42 | :return: the last item to be emitted from a BlockingObservable 43 | """ 44 | last_item = None 45 | is_empty = True 46 | 47 | for item in self.to_iterable(): 48 | is_empty = False 49 | last_item = item 50 | 51 | if is_empty: 52 | return default_value 53 | 54 | return last_item 55 | -------------------------------------------------------------------------------- /rx/linq/observable/blocking/toiterable.py: -------------------------------------------------------------------------------- 1 | from rx.core.blockingobservable import BlockingObservable 2 | from rx.internal import extensionmethod 3 | from rx.internal.enumerator import Enumerator 4 | from rx import config 5 | 6 | 7 | @extensionmethod(BlockingObservable) 8 | def to_iterable(self): 9 | """Returns an iterator that can iterate over items emitted by this 10 | `BlockingObservable`. 11 | 12 | :returns: An iterator that can iterate over the items emitted by this 13 | `BlockingObservable`. 14 | :rtype: Iterable[Any] 15 | """ 16 | 17 | condition = config["concurrency"].Condition() 18 | notifications = [] 19 | 20 | def on_next(value): 21 | """Takes on_next values and appends them to the notification queue""" 22 | 23 | condition.acquire() 24 | notifications.append(value) 25 | condition.notify() # signal that a new item is available 26 | condition.release() 27 | 28 | self.observable.materialize().subscribe(on_next) 29 | 30 | def gen(): 31 | """Generator producing values for the iterator""" 32 | 33 | while True: 34 | condition.acquire() 35 | while not len(notifications): 36 | condition.wait() 37 | notification = notifications.pop(0) 38 | 39 | if notification.kind == "E": 40 | raise notification.exception 41 | 42 | if notification.kind == "C": 43 | return # StopIteration 44 | 45 | condition.release() 46 | yield notification.value 47 | 48 | return Enumerator(gen()) 49 | 50 | 51 | @extensionmethod(BlockingObservable) 52 | def __iter__(self): 53 | """Returns an iterator that can iterate over items emitted by this 54 | `BlockingObservable`. 55 | 56 | :param BlockingObservable self: Blocking observable instance. 57 | :returns: An iterator that can iterate over the items emitted by this 58 | `BlockingObservable`. 59 | :rtype: Iterable[Any] 60 | """ 61 | 62 | return self.to_iterable() 63 | -------------------------------------------------------------------------------- /rx/linq/observable/buffer.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def buffer(self, buffer_openings=None, buffer_closing_selector=None): 7 | """Projects each element of an observable sequence into zero or more 8 | buffers. 9 | 10 | Keyword arguments: 11 | buffer_openings -- Observable sequence whose elements denote the 12 | creation of windows. 13 | buffer_closing_selector -- [optional] A function invoked to define the 14 | closing of each produced window. If a closing selector function is 15 | specified for the first parameter, this parameter is ignored. 16 | 17 | Returns an observable sequence of windows. 18 | """ 19 | 20 | return self.window(buffer_openings, buffer_closing_selector).select_many(lambda item: item.to_iterable()) 21 | 22 | 23 | @extensionmethod(Observable) 24 | def buffer_with_count(self, count, skip=None): 25 | """Projects each element of an observable sequence into zero or more 26 | buffers which are produced based on element count information. 27 | 28 | Example: 29 | res = xs.buffer_with_count(10) 30 | res = xs.buffer_with_count(10, 1) 31 | 32 | Keyword parameters: 33 | count -- {Number} Length of each buffer. 34 | skip -- {Number} [Optional] Number of elements to skip between creation 35 | of consecutive buffers. If not provided, defaults to the count. 36 | 37 | Returns an observable {Observable} sequence of buffers. 38 | """ 39 | 40 | if skip is None: 41 | skip = count 42 | 43 | def selector(x): 44 | return x.to_iterable() 45 | 46 | def predicate(x): 47 | return len(x) > 0 48 | 49 | return self.window_with_count(count, skip).select_many(selector).filter(predicate) 50 | -------------------------------------------------------------------------------- /rx/linq/observable/bufferwithtime.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def buffer_with_time(self, timespan, timeshift=None, scheduler=None): 8 | """Projects each element of an observable sequence into zero or more 9 | buffers which are produced based on timing information. 10 | 11 | # non-overlapping segments of 1 second 12 | 1 - res = xs.buffer_with_time(1000) 13 | # segments of 1 second with time shift 0.5 seconds 14 | 2 - res = xs.buffer_with_time(1000, 500) 15 | 16 | Keyword arguments: 17 | timespan -- Length of each buffer (specified as an integer denoting 18 | milliseconds). 19 | timeshift -- [Optional] Interval between creation of consecutive 20 | buffers (specified as an integer denoting milliseconds), or an 21 | optional scheduler parameter. If not specified, the time shift 22 | corresponds to the timespan parameter, resulting in non-overlapping 23 | adjacent buffers. 24 | scheduler -- [Optional] Scheduler to run buffer timers on. If not 25 | specified, the timeout scheduler is used. 26 | 27 | Returns an observable sequence of buffers. 28 | """ 29 | 30 | if not timeshift: 31 | timeshift = timespan 32 | 33 | scheduler = scheduler or timeout_scheduler 34 | 35 | return self.window_with_time(timespan, timeshift, scheduler) \ 36 | .select_many(lambda x: x.to_iterable()) 37 | -------------------------------------------------------------------------------- /rx/linq/observable/bufferwithtimeorcount.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def buffer_with_time_or_count(self, timespan, count, scheduler): 8 | """Projects each element of an observable sequence into a buffer that 9 | is completed when either it's full or a given amount of time has 10 | elapsed. 11 | 12 | # 5s or 50 items in an array 13 | 1 - res = source.buffer_with_time_or_count(5000, 50) 14 | # 5s or 50 items in an array 15 | 2 - res = source.buffer_with_time_or_count(5000, 50, Scheduler.timeout) 16 | 17 | Keyword arguments: 18 | timespan -- Maximum time length of a buffer. 19 | count -- Maximum element count of a buffer. 20 | scheduler -- [Optional] Scheduler to run bufferin timers on. If not 21 | specified, the timeout scheduler is used. 22 | 23 | Returns an observable sequence of buffers. 24 | """ 25 | 26 | scheduler = scheduler or timeout_scheduler 27 | return self.window_with_time_or_count(timespan, count, scheduler) \ 28 | .flat_map(lambda x: x.to_iterable()) 29 | -------------------------------------------------------------------------------- /rx/linq/observable/case.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable, alias="switch_case") 6 | def case(cls, selector, sources, default_source=None, scheduler=None): 7 | """Uses selector to determine which source in sources to use. 8 | There is an alias 'switch_case'. 9 | 10 | Example: 11 | 1 - res = rx.Observable.case(selector, { '1': obs1, '2': obs2 }) 12 | 2 - res = rx.Observable.case(selector, { '1': obs1, '2': obs2 }, obs0) 13 | 3 - res = rx.Observable.case(selector, { '1': obs1, '2': obs2 }, 14 | scheduler=scheduler) 15 | 16 | Keyword arguments: 17 | :param types.FunctionType selector: The function which extracts the value 18 | for to test in a case statement. 19 | :param list sources: A object which has keys which correspond to the case 20 | statement labels. 21 | :param Observable default_source: The observable sequence or Promise that 22 | will be run if the sources are not matched. If this is not provided, it 23 | defaults to rx.Observabe.empty with the specified scheduler. 24 | 25 | :returns: An observable sequence which is determined by a case statement. 26 | :rtype: Observable 27 | """ 28 | 29 | default_source = default_source or Observable.empty(scheduler=scheduler) 30 | 31 | def factory(): 32 | try: 33 | result = sources[selector()] 34 | except KeyError: 35 | result = default_source 36 | 37 | result = Observable.from_future(result) 38 | 39 | return result 40 | return Observable.defer(factory) 41 | -------------------------------------------------------------------------------- /rx/linq/observable/contains.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal.basic import default_comparer 3 | from rx.internal import extensionmethod 4 | 5 | @extensionmethod(Observable) 6 | def contains(self, value, comparer=None): 7 | """Determines whether an observable sequence contains a specified 8 | element with an optional equality comparer. 9 | 10 | Example 11 | 1 - res = source.contains(42) 12 | 2 - res = source.contains({ "value": 42 }, lambda x, y: x["value"] == y["value") 13 | 14 | Keyword parameters: 15 | value -- The value to locate in the source sequence. 16 | comparer -- {Function} [Optional] An equality comparer to compare elements. 17 | 18 | Returns an observable {Observable} sequence containing a single element 19 | determining whether the source sequence contains an element that has 20 | the specified value. 21 | """ 22 | 23 | comparer = comparer or default_comparer 24 | return self.filter(lambda v: comparer(v, value)).some() -------------------------------------------------------------------------------- /rx/linq/observable/count.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def count(self, predicate=None): 7 | """Returns an observable sequence containing a value that represents 8 | how many elements in the specified observable sequence satisfy a 9 | condition if provided, else the count of items. 10 | 11 | 1 - res = source.count() 12 | 2 - res = source.count(lambda x: x > 3) 13 | 14 | Keyword arguments: 15 | :param types.FunctionType predicate: A function to test each element for a 16 | condition. 17 | 18 | :returns: An observable sequence containing a single element with a 19 | number that represents how many elements in the input sequence satisfy 20 | the condition in the predicate function if provided, else the count of 21 | items in the sequence. 22 | :rtype: Observable 23 | """ 24 | 25 | if predicate: 26 | return self.filter(predicate).count() 27 | else: 28 | return self.reduce(lambda count, _: count + 1, seed=0) 29 | -------------------------------------------------------------------------------- /rx/linq/observable/create.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable, alias="create_with_disposable") 6 | def create(cls, subscribe): 7 | return AnonymousObservable(subscribe) 8 | -------------------------------------------------------------------------------- /rx/linq/observable/defaultifempty.py: -------------------------------------------------------------------------------- 1 | from rx import AnonymousObservable, Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def default_if_empty(self, default_value=None): 7 | """Returns the elements of the specified sequence or the specified value 8 | in a singleton sequence if the sequence is empty. 9 | 10 | res = obs = xs.defaultIfEmpty() 11 | obs = xs.defaultIfEmpty(False 12 | 13 | Keyword arguments: 14 | default_value -- The value to return if the sequence is empty. If not 15 | provided, this defaults to None. 16 | 17 | Returns an observable {Observable} sequence that contains the specified 18 | default value if the source is empty otherwise, the elements of the 19 | source itself. 20 | """ 21 | 22 | source = self 23 | 24 | def subscribe(observer): 25 | found = [False] 26 | 27 | def on_next(x): 28 | found[0] = True 29 | observer.on_next(x) 30 | 31 | def on_completed(): 32 | if not found[0]: 33 | observer.on_next(default_value) 34 | observer.on_completed() 35 | 36 | return source.subscribe(on_next, observer.on_error, on_completed) 37 | return AnonymousObservable(subscribe) 38 | -------------------------------------------------------------------------------- /rx/linq/observable/defer.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def defer(cls, observable_factory): 7 | """Returns an observable sequence that invokes the specified factory 8 | function whenever a new observer subscribes. 9 | 10 | Example: 11 | 1 - res = rx.Observable.defer(lambda: rx.Observable.from_([1,2,3])) 12 | 13 | Keyword arguments: 14 | :param types.FunctionType observable_factory: Observable factory function 15 | to invoke for each observer that subscribes to the resulting sequence. 16 | 17 | :returns: An observable sequence whose observers trigger an invocation 18 | of the given observable factory function. 19 | :rtype: Observable 20 | """ 21 | 22 | def subscribe(observer): 23 | try: 24 | result = observable_factory() 25 | except Exception as ex: 26 | return Observable.throw_exception(ex).subscribe(observer) 27 | 28 | result = Observable.from_future(result) 29 | return result.subscribe(observer) 30 | return AnonymousObservable(subscribe) 31 | -------------------------------------------------------------------------------- /rx/linq/observable/delaysubscription.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def delay_subscription(self, duetime, scheduler=None): 8 | """Time shifts the observable sequence by delaying the subscription. 9 | 10 | 1 - res = source.delay_subscription(5000) # 5s 11 | 2 - res = source.delay_subscription(5000, Scheduler.timeout) # 5 seconds 12 | 13 | duetime -- Absolute or relative time to perform the subscription at. 14 | scheduler [Optional] Scheduler to run the subscription delay timer on. 15 | If not specified, the timeout scheduler is used. 16 | 17 | Returns time-shifted sequence. 18 | """ 19 | 20 | scheduler = scheduler or timeout_scheduler 21 | 22 | def selector(_): 23 | return Observable.empty() 24 | return self.delay_with_selector(Observable.timer(duetime, scheduler), selector) 25 | -------------------------------------------------------------------------------- /rx/linq/observable/dematerialize.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def dematerialize(self): 7 | """Dematerializes the explicit notification values of an observable 8 | sequence as implicit notifications. 9 | 10 | Returns an observable sequence exhibiting the behavior corresponding to 11 | the source sequence's notification values. 12 | """ 13 | 14 | source = self 15 | 16 | def subscribe(observer): 17 | def on_next(value): 18 | return value.accept(observer) 19 | 20 | return source.subscribe(on_next, observer.on_error, observer.on_completed) 21 | return AnonymousObservable(subscribe) 22 | -------------------------------------------------------------------------------- /rx/linq/observable/dowhile.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def do_while(self, condition): 7 | """Repeats source as long as condition holds emulating a do while loop. 8 | 9 | Keyword arguments: 10 | condition -- {Function} The condition which determines if the source 11 | will be repeated. 12 | 13 | Returns an observable {Observable} sequence which is repeated as long 14 | as the condition holds. 15 | """ 16 | 17 | return Observable.concat([self, Observable.while_do(condition, self)]) 18 | -------------------------------------------------------------------------------- /rx/linq/observable/elementat.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from .elementatordefault import _element_at_or_default 5 | 6 | 7 | @extensionmethod(Observable) 8 | def element_at(self, index): 9 | """Returns the element at a specified index in a sequence. 10 | 11 | Example: 12 | res = source.element_at(5) 13 | 14 | Keyword arguments: 15 | :param int index: The zero-based index of the element to retrieve. 16 | 17 | :returns: An observable sequence that produces the element at the 18 | specified position in the source sequence. 19 | :rtype: Observable 20 | """ 21 | 22 | return _element_at_or_default(self, index, False) 23 | -------------------------------------------------------------------------------- /rx/linq/observable/elementatordefault.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.exceptions import ArgumentOutOfRangeException 3 | from rx.internal import extensionmethod 4 | 5 | 6 | def _element_at_or_default(source, index, has_default=False, 7 | default_value=None): 8 | if index < 0: 9 | raise ArgumentOutOfRangeException() 10 | 11 | def subscribe(observer): 12 | i = [index] 13 | 14 | def on_next(x): 15 | found = False 16 | with source.lock: 17 | if i[0]: 18 | i[0] -= 1 19 | else: 20 | found = True 21 | 22 | if found: 23 | observer.on_next(x) 24 | observer.on_completed() 25 | 26 | def on_completed(): 27 | if not has_default: 28 | observer.on_error(ArgumentOutOfRangeException()) 29 | else: 30 | observer.on_next(default_value) 31 | observer.on_completed() 32 | 33 | return source.subscribe(on_next, observer.on_error, on_completed) 34 | return AnonymousObservable(subscribe) 35 | 36 | @extensionmethod(Observable) 37 | def element_at_or_default(self, index, default_value=None): 38 | """Returns the element at a specified index in a sequence or a default 39 | value if the index is out of range. 40 | 41 | Example: 42 | res = source.element_at_or_default(5) 43 | res = source.element_at_or_default(5, 0) 44 | 45 | Keyword arguments: 46 | index -- {Number} The zero-based index of the element to retrieve. 47 | default_value -- [Optional] The default value if the index is outside 48 | the bounds of the source sequence. 49 | 50 | Returns an observable {Observable} sequence that produces the element at 51 | the specified position in the source sequence, or a default value if 52 | the index is outside the bounds of the source sequence. 53 | """ 54 | 55 | return _element_at_or_default(self, index, True, default_value) 56 | -------------------------------------------------------------------------------- /rx/linq/observable/empty.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.concurrency import immediate_scheduler 3 | from rx.internal import extensionclassmethod 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def empty(cls, scheduler=None): 8 | """Returns an empty observable sequence, using the specified scheduler 9 | to send out the single OnCompleted message. 10 | 11 | 1 - res = rx.Observable.empty() 12 | 2 - res = rx.Observable.empty(rx.Scheduler.timeout) 13 | 14 | scheduler -- Scheduler to send the termination call on. 15 | 16 | Returns an observable sequence with no elements. 17 | """ 18 | 19 | scheduler = scheduler or immediate_scheduler 20 | 21 | def subscribe(observer): 22 | def action(scheduler, state): 23 | observer.on_completed() 24 | 25 | return scheduler.schedule(action) 26 | return AnonymousObservable(subscribe) 27 | 28 | -------------------------------------------------------------------------------- /rx/linq/observable/exclusive.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | 3 | from rx.disposables import CompositeDisposable, SingleAssignmentDisposable 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def exclusive(self): 9 | """Performs a exclusive waiting for the first to finish before 10 | subscribing to another observable. Observables that come in between 11 | subscriptions will be dropped on the floor. 12 | 13 | Returns an exclusive observable {Observable} with only the results that 14 | happen when subscribed. 15 | """ 16 | 17 | sources = self 18 | 19 | def subscribe(observer): 20 | has_current = [False] 21 | is_stopped = [False] 22 | m = SingleAssignmentDisposable() 23 | g = CompositeDisposable() 24 | 25 | g.add(m) 26 | 27 | def on_next(inner_source): 28 | if not has_current[0]: 29 | has_current[0] = True 30 | 31 | inner_source = Observable.from_future(inner_source) 32 | 33 | inner_subscription = SingleAssignmentDisposable() 34 | g.add(inner_subscription) 35 | 36 | def on_completed_inner(): 37 | g.remove(inner_subscription) 38 | has_current[0] = False 39 | if is_stopped[0] and len(g) == 1: 40 | observer.on_completed() 41 | 42 | inner_subscription.disposable = inner_source.subscribe( 43 | observer.on_next, 44 | observer.on_error, 45 | on_completed_inner 46 | ) 47 | 48 | def on_completed(): 49 | is_stopped[0] = True 50 | if not has_current[0] and len(g) == 1: 51 | observer.on_completed() 52 | 53 | m.disposable = sources.subscribe(on_next, observer.on_error, on_completed) 54 | return g 55 | return AnonymousObservable(subscribe) 56 | -------------------------------------------------------------------------------- /rx/linq/observable/finallyaction.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable, Disposable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def finally_action(self, action): 7 | """Invokes a specified action after the source observable sequence 8 | terminates gracefully or exceptionally. 9 | 10 | Example: 11 | res = observable.finally(lambda: print('sequence ended') 12 | 13 | Keyword arguments: 14 | action -- {Function} Action to invoke after the source observable 15 | sequence terminates. 16 | Returns {Observable} Source sequence with the action-invoking 17 | termination behavior applied. 18 | """ 19 | 20 | source = self 21 | 22 | def subscribe(observer): 23 | try: 24 | subscription = source.subscribe(observer) 25 | except Exception: 26 | action() 27 | raise 28 | 29 | def dispose(): 30 | try: 31 | subscription.dispose() 32 | finally: 33 | action() 34 | 35 | return Disposable.create(dispose) 36 | return AnonymousObservable(subscribe) 37 | -------------------------------------------------------------------------------- /rx/linq/observable/find.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | def find_value(source, predicate, yield_index): 6 | def subscribe(observer): 7 | i = [0] 8 | 9 | def on_next(x): 10 | should_run = False 11 | try: 12 | should_run = predicate(x, i, source) 13 | except Exception as ex: 14 | observer.on_error(ex) 15 | return 16 | 17 | if should_run: 18 | observer.on_next(i[0] if yield_index else x) 19 | observer.on_completed() 20 | else: 21 | i[0] += 1 22 | 23 | def on_completed(): 24 | observer.on_next(-1 if yield_index else None) 25 | observer.on_completed() 26 | 27 | return source.subscribe(on_next, observer.on_error, on_completed) 28 | return AnonymousObservable(subscribe) 29 | 30 | @extensionmethod(Observable) 31 | def find(self, predicate): 32 | """Searches for an element that matches the conditions defined by the 33 | specified predicate, and returns the first occurrence within the entire 34 | Observable sequence. 35 | 36 | Keyword arguments: 37 | predicate -- {Function} The predicate that defines the conditions of the 38 | element to search for. 39 | 40 | Returns an Observable {Observable} sequence with the first element that 41 | matches the conditions defined by the specified predicate, if found 42 | otherwise, None. 43 | """ 44 | 45 | return find_value(self, predicate, False) 46 | -------------------------------------------------------------------------------- /rx/linq/observable/findindex.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from .find import find_value 5 | 6 | 7 | @extensionmethod(Observable) 8 | def find_index(self, predicate): 9 | """Searches for an element that matches the conditions defined by the 10 | specified predicate, and returns an Observable sequence with the 11 | zero-based index of the first occurrence within the entire Observable 12 | sequence. 13 | 14 | Keyword Arguments: 15 | predicate -- {Function} The predicate that defines the conditions of the 16 | element to search for. 17 | 18 | Returns an observable {Observable} sequence with the zero-based index of 19 | the first occurrence of an element that matches the conditions defined 20 | by match, if found; otherwise, -1. 21 | """ 22 | return find_value(self, predicate, True) 23 | -------------------------------------------------------------------------------- /rx/linq/observable/first.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from .firstordefault import first_or_default_async 5 | 6 | 7 | @extensionmethod(Observable) 8 | def first(self, predicate=None): 9 | """Returns the first element of an observable sequence that satisfies 10 | the condition in the predicate if present else the first item in the 11 | sequence. 12 | 13 | Example: 14 | res = res = source.first() 15 | res = res = source.first(lambda x: x > 3) 16 | 17 | Keyword arguments: 18 | predicate -- {Function} [Optional] A predicate function to evaluate for 19 | elements in the source sequence. 20 | 21 | Returns {Observable} Sequence containing the first element in the 22 | observable sequence that satisfies the condition in the predicate if 23 | provided, else the first item in the sequence. 24 | """ 25 | 26 | return self.filter(predicate).first() if predicate else first_or_default_async(self, False) 27 | -------------------------------------------------------------------------------- /rx/linq/observable/firstordefault.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.exceptions import SequenceContainsNoElementsError 3 | from rx.internal import extensionmethod 4 | 5 | def first_or_default_async(source, has_default=False, default_value=None): 6 | def subscribe(observer): 7 | def on_next(x): 8 | observer.on_next(x) 9 | observer.on_completed() 10 | 11 | def on_completed(): 12 | if not has_default: 13 | observer.on_error(SequenceContainsNoElementsError()) 14 | else: 15 | observer.on_next(default_value) 16 | observer.on_completed() 17 | 18 | return source.subscribe(on_next, observer.on_error, on_completed) 19 | return AnonymousObservable(subscribe) 20 | 21 | 22 | @extensionmethod(Observable) 23 | def first_or_default(self, predicate=None, default_value=None): 24 | """Returns the first element of an observable sequence that satisfies 25 | the condition in the predicate, or a default value if no such element 26 | exists. 27 | 28 | Example: 29 | res = source.first_or_default() 30 | res = source.first_or_default(lambda x: x > 3) 31 | res = source.first_or_default(lambda x: x > 3, 0) 32 | res = source.first_or_default(null, 0) 33 | 34 | Keyword arguments: 35 | predicate -- {Function} [optional] A predicate function to evaluate for 36 | elements in the source sequence. 37 | default_value -- {Any} [Optional] The default value if no such element 38 | exists. If not specified, defaults to None. 39 | 40 | Returns {Observable} Sequence containing the first element in the 41 | observable sequence that satisfies the condition in the predicate, or a 42 | default value if no such element exists. 43 | """ 44 | 45 | return self.filter(predicate).first_or_default(None, default_value) if predicate else first_or_default_async(self, True, default_value) 46 | -------------------------------------------------------------------------------- /rx/linq/observable/forin.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionclassmethod 3 | from rx.internal import Enumerable 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def for_in(cls, sources, result_selector): 8 | """Concatenates the observable sequences obtained by running the 9 | specified result selector for each element in source. 10 | 11 | sources -- {Array} An array of values to turn into an observable 12 | sequence. 13 | result_selector -- {Function} A function to apply to each item in the 14 | sources array to turn it into an observable sequence. 15 | Returns an observable {Observable} sequence from the concatenated 16 | observable sequences. 17 | """ 18 | 19 | return Observable.concat(Enumerable.for_each(sources, result_selector)) 20 | -------------------------------------------------------------------------------- /rx/linq/observable/fromcallback.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def from_callback(cls, func, selector=None): 7 | """Converts a callback function to an observable sequence. 8 | 9 | Keyword arguments: 10 | func -- {Function} Function with a callback as the last parameter to 11 | convert to an Observable sequence. 12 | selector -- {Function} [Optional] A selector which takes the arguments 13 | from the callback to produce a single item to yield on next. 14 | 15 | Returns {Function} A function, when executed with the required 16 | parameters minus the callback, produces an Observable sequence with a 17 | single value of the arguments to the callback as a list. 18 | """ 19 | 20 | def function(*args): 21 | arguments = list(args) 22 | 23 | def subscribe(observer): 24 | def handler(*args): 25 | results = list(args) 26 | if selector: 27 | try: 28 | results = selector(args) 29 | except Exception as err: 30 | observer.on_error(err) 31 | return 32 | 33 | observer.on_next(results) 34 | else: 35 | if isinstance(results, list) and len(results) <= 1: 36 | observer.on_next(*results) 37 | else: 38 | observer.on_next(results) 39 | 40 | observer.on_completed() 41 | 42 | arguments.append(handler) 43 | func(*arguments) 44 | 45 | return AnonymousObservable(subscribe) 46 | return function 47 | -------------------------------------------------------------------------------- /rx/linq/observable/fromfuture.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal.utils import is_future 3 | from rx.internal import extensionclassmethod 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def from_future(cls, future): 8 | """Converts a Future to an Observable sequence 9 | 10 | Keyword Arguments: 11 | future -- {Future} A Python 3 compatible future. 12 | https://docs.python.org/3/library/asyncio-task.html#future 13 | http://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.Future 14 | 15 | Returns {Observable} An Observable sequence which wraps the existing 16 | future success and failure. 17 | """ 18 | 19 | def subscribe(observer): 20 | def done(future): 21 | try: 22 | value = future.result() 23 | except Exception as ex: 24 | observer.on_error(ex) 25 | else: 26 | observer.on_next(value) 27 | observer.on_completed() 28 | 29 | future.add_done_callback(done) 30 | 31 | def dispose(): 32 | if future and future.cancel: 33 | future.cancel() 34 | 35 | return dispose 36 | 37 | return AnonymousObservable(subscribe) if is_future(future) else future 38 | -------------------------------------------------------------------------------- /rx/linq/observable/fromiterable.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | from rx.core import Observable, AnonymousObservable 3 | from rx.concurrency import current_thread_scheduler 4 | from rx.disposables import MultipleAssignmentDisposable 5 | from rx.internal import extensionclassmethod 6 | 7 | 8 | @extensionclassmethod(Observable, alias=["from_", "from_list"]) 9 | def from_iterable(cls, iterable, scheduler=None): 10 | """Converts an array to an observable sequence, using an optional 11 | scheduler to enumerate the array. 12 | 13 | 1 - res = rx.Observable.from_iterable([1,2,3]) 14 | 2 - res = rx.Observable.from_iterable([1,2,3], rx.Scheduler.timeout) 15 | 16 | Keyword arguments: 17 | :param Observable cls: Observable class 18 | :param Scheduler scheduler: [Optional] Scheduler to run the 19 | enumeration of the input sequence on. 20 | 21 | :returns: The observable sequence whose elements are pulled from the 22 | given enumerable sequence. 23 | :rtype: Observable 24 | """ 25 | 26 | scheduler = scheduler or current_thread_scheduler 27 | lock = config["concurrency"].RLock() 28 | 29 | def subscribe(observer): 30 | sd = MultipleAssignmentDisposable() 31 | iterator = iter(iterable) 32 | 33 | def action(scheduler, state=None): 34 | try: 35 | with lock: 36 | item = next(iterator) 37 | 38 | except StopIteration: 39 | observer.on_completed() 40 | else: 41 | observer.on_next(item) 42 | sd.disposable = scheduler.schedule(action) 43 | 44 | sd.disposable = scheduler.schedule(action) 45 | return sd 46 | return AnonymousObservable(subscribe) 47 | -------------------------------------------------------------------------------- /rx/linq/observable/groupby.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def group_by(self, key_selector, element_selector=None, 7 | key_serializer=None): 8 | """Groups the elements of an observable sequence according to a 9 | specified key selector function and comparer and selects the resulting 10 | elements by using a specified function. 11 | 12 | 1 - observable.group_by(lambda x: x.id) 13 | 2 - observable.group_by(lambda x: x.id, lambda x: x.name) 14 | 3 - observable.group_by( 15 | lambda x: x.id, 16 | lambda x: x.name, 17 | lambda x: str(x)) 18 | 19 | Keyword arguments: 20 | key_selector -- A function to extract the key for each element. 21 | element_selector -- [Optional] A function to map each source element to 22 | an element in an observable group. 23 | comparer -- {Function} [Optional] Used to determine whether the objects 24 | are equal. 25 | 26 | Returns a sequence of observable groups, each of which corresponds to a 27 | unique key value, containing all elements that share that same key 28 | value. 29 | """ 30 | 31 | def duration_selector(x): 32 | return Observable.never() 33 | 34 | return self.group_by_until(key_selector, element_selector, duration_selector, key_serializer) 35 | -------------------------------------------------------------------------------- /rx/linq/observable/ifthen.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def if_then(cls, condition, then_source, else_source=None, scheduler=None): 7 | """Determines whether an observable collection contains values. 8 | 9 | Example: 10 | 1 - res = rx.Observable.if(condition, obs1) 11 | 2 - res = rx.Observable.if(condition, obs1, obs2) 12 | 3 - res = rx.Observable.if(condition, obs1, scheduler=scheduler) 13 | 14 | Keyword parameters: 15 | condition -- {Function} The condition which determines if the 16 | then_source or else_source will be run. 17 | then_source -- {Observable} The observable sequence or Promise that 18 | will be run if the condition function returns true. 19 | else_source -- {Observable} [Optional] The observable sequence or 20 | Promise that will be run if the condition function returns False. 21 | If this is not provided, it defaults to rx.Observable.empty 22 | scheduler -- [Optional] Scheduler to use. 23 | 24 | Returns an observable {Observable} sequence which is either the 25 | then_source or else_source. 26 | """ 27 | 28 | else_source = else_source or Observable.empty(scheduler=scheduler) 29 | 30 | then_source = Observable.from_future(then_source) 31 | else_source = Observable.from_future(else_source) 32 | 33 | def factory(): 34 | return then_source if condition() else else_source 35 | return Observable.defer(factory) 36 | -------------------------------------------------------------------------------- /rx/linq/observable/ignoreelements.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import noop 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def ignore_elements(self): 8 | """Ignores all elements in an observable sequence leaving only the 9 | termination messages. 10 | 11 | Returns an empty observable {Observable} sequence that signals 12 | termination, successful or exceptional, of the source sequence. 13 | """ 14 | 15 | source = self 16 | 17 | def subscribe(observer): 18 | return source.subscribe(noop, observer.on_error, observer.on_completed) 19 | 20 | return AnonymousObservable(subscribe) 21 | -------------------------------------------------------------------------------- /rx/linq/observable/interval.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.concurrency import TimeoutScheduler 3 | from rx.internal import extensionclassmethod 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def interval(cls, period, scheduler=None): 8 | """Returns an observable sequence that produces a value after each 9 | period. 10 | 11 | Example: 12 | 1 - res = rx.Observable.interval(1000) 13 | 2 - res = rx.Observable.interval(1000, rx.Scheduler.timeout) 14 | 15 | Keyword arguments: 16 | period -- Period for producing the values in the resulting sequence 17 | (specified as an integer denoting milliseconds). 18 | scheduler -- [Optional] Scheduler to run the timer on. If not specified, 19 | rx.Scheduler.timeout is used. 20 | 21 | Returns an observable sequence that produces a value after each period. 22 | """ 23 | 24 | scheduler = scheduler or TimeoutScheduler() 25 | return Observable.timer(period, period, scheduler) 26 | -------------------------------------------------------------------------------- /rx/linq/observable/isempty.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | @extensionmethod(Observable) 5 | def is_empty(self): 6 | """Determines whether an observable sequence is empty. 7 | 8 | Returns an observable {Observable} sequence containing a single element 9 | determining whether the source sequence is empty. 10 | """ 11 | 12 | return self.some().map(lambda b: not b) 13 | 14 | -------------------------------------------------------------------------------- /rx/linq/observable/last.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from .lastordefault import last_or_default_async 5 | 6 | 7 | @extensionmethod(Observable) 8 | def last(self, predicate=None): 9 | """Returns the last element of an observable sequence that satisfies the 10 | condition in the predicate if specified, else the last element. 11 | 12 | Example: 13 | res = source.last() 14 | res = source.last(lambda x: x > 3) 15 | 16 | Keyword arguments: 17 | predicate -- {Function} [Optional] A predicate function to evaluate for 18 | elements in the source sequence. 19 | 20 | Returns {Observable} Sequence containing the last element in the 21 | observable sequence that satisfies the condition in the predicate. 22 | """ 23 | 24 | return self.filter(predicate).last() if predicate else last_or_default_async(self, False) 25 | -------------------------------------------------------------------------------- /rx/linq/observable/lastordefault.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.exceptions import SequenceContainsNoElementsError 3 | from rx.internal import extensionmethod 4 | 5 | 6 | def last_or_default_async(source, has_default=False, default_value=None): 7 | def subscribe(observer): 8 | value = [default_value] 9 | seen_value = [False] 10 | 11 | def on_next(x): 12 | value[0] = x 13 | seen_value[0] = True 14 | 15 | def on_completed(): 16 | if not seen_value[0] and not has_default: 17 | observer.on_error(SequenceContainsNoElementsError()) 18 | else: 19 | observer.on_next(value[0]) 20 | observer.on_completed() 21 | 22 | return source.subscribe(on_next, observer.on_error, on_completed) 23 | return AnonymousObservable(subscribe) 24 | 25 | 26 | @extensionmethod(Observable) 27 | def last_or_default(self, predicate=None, default_value=None): 28 | """Return last or default element. 29 | 30 | Returns the last element of an observable sequence that satisfies 31 | the condition in the predicate, or a default value if no such 32 | element exists. 33 | 34 | Examples: 35 | res = source.last_or_default() 36 | res = source.last_or_default(lambda x: x > 3) 37 | res = source.last_or_default(lambda x: x > 3, 0) 38 | res = source.last_or_default(None, 0) 39 | 40 | predicate -- {Function} [Optional] A predicate function to evaluate 41 | for elements in the source sequence. 42 | default_value -- [Optional] The default value if no such element 43 | exists. If not specified, defaults to None. 44 | 45 | Returns {Observable} Sequence containing the last element in the 46 | observable sequence that satisfies the condition in the predicate, 47 | or a default value if no such element exists. 48 | """ 49 | if predicate: 50 | return self.filter(predicate).last_or_default(None, default_value) 51 | 52 | return last_or_default_async(self, True, default_value) 53 | -------------------------------------------------------------------------------- /rx/linq/observable/let.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable, alias="let") 6 | def let_bind(self, func): 7 | """Returns an observable sequence that is the result of invoking the 8 | selector on the source sequence, without sharing subscriptions. This 9 | operator allows for a fluent style of writing queries that use the same 10 | sequence multiple times. 11 | 12 | selector -- {Function} Selector function which can use the source 13 | sequence as many times as needed, without sharing subscriptions to 14 | the source sequence. 15 | 16 | Returns an observable {Observable} sequence that contains the elements 17 | of a sequence produced by multicasting the source sequence within a 18 | selector function. 19 | """ 20 | 21 | return func(self) 22 | -------------------------------------------------------------------------------- /rx/linq/observable/materialize.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.core.notification import OnNext, OnError, OnCompleted 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def materialize(self): 8 | """Materializes the implicit notifications of an observable sequence as 9 | explicit notification values. 10 | 11 | Returns an observable sequence containing the materialized notification 12 | values from the source sequence. 13 | """ 14 | 15 | source = self 16 | 17 | def subscribe(observer): 18 | def on_next(value): 19 | observer.on_next(OnNext(value)) 20 | 21 | def on_error(exception): 22 | observer.on_next(OnError(exception)) 23 | observer.on_completed() 24 | 25 | def on_completed(): 26 | observer.on_next(OnCompleted()) 27 | observer.on_completed() 28 | 29 | return source.subscribe(on_next, on_error, on_completed) 30 | return AnonymousObservable(subscribe) 31 | 32 | -------------------------------------------------------------------------------- /rx/linq/observable/max.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal.basic import identity 3 | from rx.internal import extensionmethod 4 | 5 | from .min import first_only 6 | 7 | 8 | @extensionmethod(Observable) 9 | def max(self, comparer=None): 10 | """Returns the maximum value in an observable sequence according to the 11 | specified comparer. 12 | 13 | Example 14 | res = source.max() 15 | res = source.max(lambda x, y: x.value - y.value) 16 | 17 | Keyword arguments: 18 | comparer -- {Function} [Optional] Comparer used to compare elements. 19 | 20 | Returns {Observable} An observable sequence containing a single element 21 | with the maximum element in the source sequence. 22 | """ 23 | 24 | return self.max_by(identity, comparer).map(lambda x: first_only(x)) 25 | -------------------------------------------------------------------------------- /rx/linq/observable/maxby.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal.basic import default_sub_comparer 3 | from rx.internal import extensionmethod 4 | 5 | from .minby import extrema_by 6 | 7 | @extensionmethod(Observable) 8 | def max_by(self, key_selector, comparer=None): 9 | """Returns the elements in an observable sequence with the maximum 10 | key value according to the specified comparer. 11 | 12 | Example 13 | res = source.max_by(lambda x: x.value) 14 | res = source.max_by(lambda x: x.value, lambda x, y: x - y) 15 | 16 | Keyword arguments: 17 | key_selector -- {Function} Key selector function. 18 | comparer -- {Function} [Optional] Comparer used to compare key values. 19 | 20 | Returns an observable {Observable} sequence containing a list of zero 21 | or more elements that have a maximum key value. 22 | """ 23 | 24 | comparer = comparer or default_sub_comparer 25 | return extrema_by(self, key_selector, comparer) 26 | -------------------------------------------------------------------------------- /rx/linq/observable/min.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | from rx.internal.basic import identity 4 | from rx.internal.exceptions import SequenceContainsNoElementsError 5 | 6 | 7 | def first_only(x): 8 | if not len(x): 9 | raise SequenceContainsNoElementsError() 10 | 11 | return x[0] 12 | 13 | 14 | @extensionmethod(Observable) 15 | def min(self, comparer=None): 16 | """Returns the minimum element in an observable sequence according to 17 | the optional comparer else a default greater than less than check. 18 | 19 | Example 20 | res = source.min() 21 | res = source.min(lambda x, y: x.value - y.value) 22 | 23 | comparer -- {Function} [Optional] Comparer used to compare elements. 24 | 25 | Returns an observable sequence {Observable} containing a single element 26 | with the minimum element in the source sequence. 27 | """ 28 | 29 | return self.min_by(identity, comparer).map(first_only) 30 | -------------------------------------------------------------------------------- /rx/linq/observable/multicast.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.linq.connectableobservable import ConnectableObservable 3 | from rx.disposables import CompositeDisposable 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def multicast(self, subject=None, subject_selector=None, selector=None): 9 | """Multicasts the source sequence notifications through an instantiated 10 | subject into all uses of the sequence within a selector function. Each 11 | subscription to the resulting sequence causes a separate multicast 12 | invocation, exposing the sequence resulting from the selector function's 13 | invocation. For specializations with fixed subject types, see Publish, 14 | PublishLast, and Replay. 15 | 16 | Example: 17 | 1 - res = source.multicast(observable) 18 | 2 - res = source.multicast(subject_selector=lambda: Subject(), 19 | selector=lambda x: x) 20 | 21 | Keyword arguments: 22 | subject_selector -- {Function} Factory function to create an 23 | intermediate subject through which the source sequence's elements 24 | will be multicast to the selector function. 25 | subject -- Subject {Subject} to push source elements into. 26 | selector -- {Function} [Optional] Optional selector function which can 27 | use the multicasted source sequence subject to the policies enforced 28 | by the created subject. Specified only if subject_selector" is a 29 | factory function. 30 | 31 | Returns an observable {Observable} sequence that contains the elements 32 | of a sequence produced by multicasting the source sequence within a 33 | selector function. 34 | """ 35 | 36 | source = self 37 | if subject_selector: 38 | def subscribe(observer): 39 | connectable = source.multicast(subject=subject_selector()) 40 | return CompositeDisposable(selector(connectable).subscribe(observer), connectable.connect()) 41 | 42 | return AnonymousObservable(subscribe) 43 | else: 44 | return ConnectableObservable(source, subject) 45 | -------------------------------------------------------------------------------- /rx/linq/observable/never.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable, Disposable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def never(cls): 7 | """Returns a non-terminating observable sequence, which can be used to 8 | denote an infinite duration (e.g. when using reactive joins). 9 | 10 | Returns an observable sequence whose observers will never get called. 11 | """ 12 | 13 | def subscribe(_): 14 | return Disposable.empty() 15 | 16 | return AnonymousObservable(subscribe) 17 | -------------------------------------------------------------------------------- /rx/linq/observable/observeon.py: -------------------------------------------------------------------------------- 1 | from rx.core import AnonymousObservable, Observable 2 | from rx.internal import extensionmethod 3 | from rx.core.observeonobserver import ObserveOnObserver 4 | 5 | 6 | @extensionmethod(Observable) 7 | def observe_on(self, scheduler): 8 | """Wraps the source sequence in order to run its observer callbacks on 9 | the specified scheduler. 10 | 11 | Keyword arguments: 12 | scheduler -- Scheduler to notify observers on. 13 | 14 | Returns the source sequence whose observations happen on the specified 15 | scheduler. 16 | 17 | This only invokes observer callbacks on a scheduler. In case the 18 | subscription and/or unsubscription actions have side-effects 19 | that require to be run on a scheduler, use subscribe_on. 20 | """ 21 | 22 | source = self 23 | 24 | def subscribe(observer): 25 | return source.subscribe(ObserveOnObserver(scheduler, observer)) 26 | 27 | return AnonymousObservable(subscribe) 28 | -------------------------------------------------------------------------------- /rx/linq/observable/of.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def of(cls, *args, **kwargs): 7 | """This method creates a new Observable instance with a variable number 8 | of arguments, regardless of number or type of the arguments. 9 | 10 | Example: 11 | res = rx.Observable.of(1,2,3) 12 | 13 | Returns the observable sequence whose elements are pulled from the given 14 | arguments 15 | """ 16 | return Observable.from_iterable(args, scheduler=kwargs.get("scheduler")) 17 | -------------------------------------------------------------------------------- /rx/linq/observable/pairwise.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def pairwise(self): 7 | """Returns a new observable that triggers on the second and subsequent 8 | triggerings of the input observable. The Nth triggering of the input 9 | observable passes the arguments from the N-1th and Nth triggering as a 10 | pair. The argument passed to the N-1th triggering is held in hidden 11 | internal state until the Nth triggering occurs. 12 | 13 | Returns an observable {Observable} that triggers on successive pairs of 14 | observations from the input observable as an array. 15 | """ 16 | 17 | source = self 18 | 19 | def subscribe(observer): 20 | has_previous = [False] 21 | previous = [None] 22 | 23 | def on_next(x): 24 | pair = None 25 | 26 | with self.lock: 27 | if has_previous[0]: 28 | pair = (previous[0], x) 29 | else: 30 | has_previous[0] = True 31 | 32 | previous[0] = x 33 | 34 | if pair: 35 | observer.on_next(pair) 36 | 37 | return source.subscribe(on_next, observer.on_error, observer.on_completed) 38 | return AnonymousObservable(subscribe) 39 | -------------------------------------------------------------------------------- /rx/linq/observable/partition.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | from rx.internal.utils import adapt_call 4 | 5 | 6 | @extensionmethod(Observable) 7 | def partition(self, predicate): 8 | """Returns two observables which partition the observations of the 9 | source by the given function. The first will trigger observations for 10 | those values for which the predicate returns true. The second will 11 | trigger observations for those values where the predicate returns false. 12 | The predicate is executed once for each subscribed observer. Both also 13 | propagate all error observations arising from the source and each 14 | completes when the source completes. 15 | 16 | Keyword arguments: 17 | predicate -- The function to determine which output Observable will 18 | trigger a particular observation. 19 | 20 | Returns a list of observables. The first triggers when the predicate 21 | returns True, and the second triggers when the predicate returns False. 22 | """ 23 | 24 | published = self.publish().ref_count() 25 | return [ 26 | published.filter(predicate), # where does adapt_call itself 27 | published.filter(lambda x, i: not adapt_call(predicate)(x, i)) 28 | ] 29 | 30 | -------------------------------------------------------------------------------- /rx/linq/observable/pluck.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def pluck(self, key): 7 | """Retrieves the value of a specified key using dict-like access (as in 8 | element[key]) from all elements in the Observable sequence. 9 | 10 | Keyword arguments: 11 | key {String} The key to pluck. 12 | 13 | Returns a new Observable {Observable} sequence of key values. 14 | 15 | To pluck an attribute of each element, use pluck_attr. 16 | 17 | """ 18 | 19 | return self.map(lambda x: x[key]) 20 | 21 | 22 | @extensionmethod(Observable) 23 | def pluck_attr(self, property): 24 | """Retrieves the value of a specified property (using getattr) from all 25 | elements in the Observable sequence. 26 | 27 | Keyword arguments: 28 | property {String} The property to pluck. 29 | 30 | Returns a new Observable {Observable} sequence of property values. 31 | 32 | To pluck values using dict-like access (as in element[key]) on each 33 | element, use pluck. 34 | 35 | """ 36 | 37 | return self.map(lambda x: getattr(x, property)) 38 | -------------------------------------------------------------------------------- /rx/linq/observable/publish.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.subjects import Subject 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def publish(self, selector=None): 8 | """Returns an observable sequence that is the result of invoking the 9 | selector on a connectable observable sequence that shares a single 10 | subscription to the underlying sequence. This operator is a 11 | specialization of Multicast using a regular Subject. 12 | 13 | Example: 14 | res = source.publish() 15 | res = source.publish(lambda x: x) 16 | 17 | selector -- {Function} [Optional] Selector function which can use the 18 | multicasted source sequence as many times as needed, without causing 19 | multiple subscriptions to the source sequence. Subscribers to the 20 | given source will receive all notifications of the source from the 21 | time of the subscription on. 22 | 23 | Returns an observable {Observable} sequence that contains the elements 24 | of a sequence produced by multicasting the source sequence within a 25 | selector function.""" 26 | 27 | if selector: 28 | return self.multicast(subject_selector=lambda: Subject(), selector=selector) 29 | else: 30 | return self.multicast(subject=Subject()) 31 | 32 | 33 | @extensionmethod(Observable) 34 | def share(self): 35 | """Share a single subscription among multple observers. 36 | 37 | Returns a new Observable that multicasts (shares) the original 38 | Observable. As long as there is at least one Subscriber this 39 | Observable will be subscribed and emitting data. When all 40 | subscribers have unsubscribed it will unsubscribe from the source 41 | Observable. 42 | 43 | This is an alias for Observable.publish().ref_count(). 44 | """ 45 | return self.publish().ref_count() 46 | -------------------------------------------------------------------------------- /rx/linq/observable/publishvalue.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.subjects import BehaviorSubject 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def publish_value(self, initial_value, selector=None): 8 | """Returns an observable sequence that is the result of invoking the 9 | selector on a connectable observable sequence that shares a single 10 | subscription to the underlying sequence and starts with initial_value. 11 | 12 | This operator is a specialization of Multicast using a BehaviorSubject. 13 | 14 | Example: 15 | res = source.publish_value(42) 16 | res = source.publish_value(42, lambda x: x.map(lambda y: y * y)) 17 | 18 | Keyword arguments: 19 | initial_value -- {Mixed} Initial value received by observers upon 20 | subscription. 21 | selector -- {Function} [Optional] Optional selector function which can 22 | use the multicasted source sequence as many times as needed, without 23 | causing multiple subscriptions to the source sequence. Subscribers 24 | to the given source will receive immediately receive the initial 25 | value, followed by all notifications of the source from the time of 26 | the subscription on. 27 | 28 | Returns {Observable} An observable sequence that contains the elements 29 | of a sequence produced by multicasting the source sequence within a 30 | selector function. 31 | """ 32 | 33 | if selector: 34 | def subject_selector(): 35 | return BehaviorSubject(initial_value) 36 | 37 | return self.multicast(subject_selector=subject_selector, 38 | selector=selector) 39 | else: 40 | return self.multicast(BehaviorSubject(initial_value)) 41 | -------------------------------------------------------------------------------- /rx/linq/observable/range.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.concurrency import current_thread_scheduler 3 | from rx.internal import extensionclassmethod 4 | from rx.disposables import MultipleAssignmentDisposable 5 | 6 | 7 | @extensionclassmethod(Observable) 8 | def range(cls, start, count, scheduler=None): 9 | """Generates an observable sequence of integral numbers within a 10 | specified range, using the specified scheduler to send out observer 11 | messages. 12 | 13 | 1 - res = Rx.Observable.range(0, 10) 14 | 2 - res = Rx.Observable.range(0, 10, rx.Scheduler.timeout) 15 | 16 | Keyword arguments: 17 | start -- The value of the first integer in the sequence. 18 | count -- The number of sequential integers to generate. 19 | scheduler -- [Optional] Scheduler to run the generator loop on. If not 20 | specified, defaults to Scheduler.current_thread. 21 | 22 | Returns an observable sequence that contains a range of sequential 23 | integral numbers. 24 | """ 25 | scheduler = scheduler or current_thread_scheduler 26 | end = start + count 27 | 28 | def subscribe(observer): 29 | sd = MultipleAssignmentDisposable() 30 | 31 | def action(scheduler, n): 32 | if n < end: 33 | observer.on_next(n) 34 | sd.disposable = scheduler.schedule(action, n + 1) 35 | else: 36 | observer.on_completed() 37 | 38 | sd.disposable = scheduler.schedule(action, start) 39 | return sd 40 | return AnonymousObservable(subscribe) 41 | -------------------------------------------------------------------------------- /rx/linq/observable/reduce.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable, alias="aggregate") 6 | def reduce(self, accumulator, seed=None): 7 | """Applies an accumulator function over an observable sequence, 8 | returning the result of the aggregation as a single element in the 9 | result sequence. The specified seed value is used as the initial 10 | accumulator value. 11 | 12 | For aggregation behavior with incremental intermediate results, see 13 | Observable.scan. 14 | 15 | Example: 16 | 1 - res = source.reduce(lambda acc, x: acc + x) 17 | 2 - res = source.reduce(lambda acc, x: acc + x, 0) 18 | 19 | Keyword arguments: 20 | :param types.FunctionType accumulator: An accumulator function to be 21 | invoked on each element. 22 | :param T seed: Optional initial accumulator value. 23 | 24 | :returns: An observable sequence containing a single element with the 25 | final accumulator value. 26 | :rtype: Observable 27 | """ 28 | 29 | if seed is not None: 30 | return self.scan(accumulator, seed=seed).start_with(seed).last() 31 | else: 32 | return self.scan(accumulator).last() 33 | -------------------------------------------------------------------------------- /rx/linq/observable/replay.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.subjects import ReplaySubject 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def replay(self, selector, buffer_size=None, window=None, scheduler=None): 8 | """Returns an observable sequence that is the result of invoking the 9 | selector on a connectable observable sequence that shares a single 10 | subscription to the underlying sequence replaying notifications subject 11 | to a maximum time length for the replay buffer. 12 | 13 | This operator is a specialization of Multicast using a ReplaySubject. 14 | 15 | Example: 16 | res = source.replay(buffer_size=3) 17 | res = source.replay(buffer_size=3, window=500) 18 | res = source.replay(None, 3, 500, scheduler) 19 | res = source.replay(lambda x: x.take(6).repeat(), 3, 500, scheduler) 20 | 21 | Keyword arguments: 22 | selector -- [Optional] Selector function which can use the multicasted 23 | source sequence as many times as needed, without causing multiple 24 | subscriptions to the source sequence. Subscribers to the given 25 | source will receive all the notifications of the source subject to 26 | the specified replay buffer trimming policy. 27 | buffer_size -- [Optional] Maximum element count of the replay buffer. 28 | window -- [Optional] Maximum time length of the replay buffer. 29 | scheduler -- [Optional] Scheduler where connected observers within the 30 | selector function will be invoked on. 31 | 32 | Returns {Observable} An observable sequence that contains the elements 33 | of a sequence produced by multicasting the source sequence within a 34 | selector function. 35 | """ 36 | 37 | if callable(selector): 38 | def subject_selector(): 39 | return ReplaySubject(buffer_size, window, scheduler) 40 | return self.multicast(subject_selector=subject_selector, 41 | selector=selector) 42 | else: 43 | return self.multicast(ReplaySubject(buffer_size, window, scheduler)) 44 | 45 | -------------------------------------------------------------------------------- /rx/linq/observable/retry.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal.enumerable import Enumerable 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def retry(self, retry_count=None): 8 | """Repeats the source observable sequence the specified number of times 9 | or until it successfully terminates. If the retry count is not 10 | specified, it retries indefinitely. 11 | 12 | 1 - retried = retry.repeat() 13 | 2 - retried = retry.repeat(42) 14 | 15 | retry_count -- [Optional] Number of times to retry the sequence. If not 16 | provided, retry the sequence indefinitely. 17 | 18 | Returns an observable sequence producing the elements of the given 19 | sequence repeatedly until it terminates successfully. 20 | """ 21 | 22 | return Observable.catch_exception(Enumerable.repeat(self, retry_count)) 23 | -------------------------------------------------------------------------------- /rx/linq/observable/sample.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.disposables import CompositeDisposable 3 | from rx.concurrency import timeout_scheduler 4 | from rx.internal import extensionmethod 5 | 6 | 7 | def sample_observable(source, sampler): 8 | 9 | def subscribe(observer): 10 | at_end = [None] 11 | has_value = [None] 12 | value = [None] 13 | 14 | def sample_subscribe(x=None): 15 | if has_value[0]: 16 | has_value[0] = False 17 | observer.on_next(value[0]) 18 | 19 | if at_end[0]: 20 | observer.on_completed() 21 | 22 | def on_next(new_value): 23 | has_value[0] = True 24 | value[0] = new_value 25 | 26 | def on_completed(): 27 | at_end[0] = True 28 | 29 | return CompositeDisposable( 30 | source.subscribe(on_next, observer.on_error, on_completed), 31 | sampler.subscribe(sample_subscribe, observer.on_error, sample_subscribe) 32 | ) 33 | return AnonymousObservable(subscribe) 34 | 35 | 36 | @extensionmethod(Observable, alias="throttle_last") 37 | def sample(self, interval=None, sampler=None, scheduler=None): 38 | """Samples the observable sequence at each interval. 39 | 40 | 1 - res = source.sample(sample_observable) # Sampler tick sequence 41 | 2 - res = source.sample(5000) # 5 seconds 42 | 2 - res = source.sample(5000, rx.scheduler.timeout) # 5 seconds 43 | 44 | Keyword arguments: 45 | source -- Source sequence to sample. 46 | interval -- Interval at which to sample (specified as an integer 47 | denoting milliseconds). 48 | scheduler -- [Optional] Scheduler to run the sampling timer on. If not 49 | specified, the timeout scheduler is used. 50 | 51 | Returns sampled observable sequence. 52 | """ 53 | 54 | scheduler = scheduler or timeout_scheduler 55 | if interval is not None: 56 | return sample_observable(self, Observable.interval(interval, scheduler=scheduler)) 57 | 58 | return sample_observable(self, sampler) 59 | -------------------------------------------------------------------------------- /rx/linq/observable/scan.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def scan(self, accumulator, seed=None): 7 | """Applies an accumulator function over an observable sequence and 8 | returns each intermediate result. The optional seed value is used as 9 | the initial accumulator value. For aggregation behavior with no 10 | intermediate results, see Observable.aggregate. 11 | 12 | 1 - scanned = source.scan(lambda acc, x: acc + x) 13 | 2 - scanned = source.scan(lambda acc, x: acc + x, 0) 14 | 15 | Keyword arguments: 16 | accumulator -- An accumulator function to be invoked on each element. 17 | seed -- [Optional] The initial accumulator value. 18 | 19 | Returns an observable sequence containing the accumulated values. 20 | """ 21 | 22 | has_seed = False 23 | if not seed is None: 24 | has_seed = True 25 | 26 | source = self 27 | 28 | def defer(): 29 | has_accumulation = [False] 30 | accumulation = [None] 31 | 32 | def projection(x): 33 | if has_accumulation[0]: 34 | accumulation[0] = accumulator(accumulation[0], x) 35 | else: 36 | accumulation[0] = accumulator(seed, x) if has_seed else x 37 | has_accumulation[0] = True 38 | 39 | return accumulation[0] 40 | return source.map(projection) 41 | return Observable.defer(defer) 42 | -------------------------------------------------------------------------------- /rx/linq/observable/select.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.utils import adapt_call 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable, alias="map") 7 | def select(self, selector): 8 | """Project each element of an observable sequence into a new form 9 | by incorporating the element's index. 10 | 11 | 1 - source.map(lambda value: value * value) 12 | 2 - source.map(lambda value, index: value * value + index) 13 | 14 | Keyword arguments: 15 | :param Callable[[Any, Any], Any] selector: A transform function to 16 | apply to each source element; the second parameter of the 17 | function represents the index of the source element. 18 | :rtype: Observable 19 | 20 | Returns an observable sequence whose elements are the result of 21 | invoking the transform function on each element of source. 22 | """ 23 | 24 | selector = adapt_call(selector) 25 | 26 | def subscribe(observer): 27 | count = [0] 28 | 29 | def on_next(value): 30 | try: 31 | result = selector(value, count[0]) 32 | except Exception as err: 33 | observer.on_error(err) 34 | else: 35 | count[0] += 1 36 | observer.on_next(result) 37 | 38 | return self.subscribe(on_next, observer.on_error, observer.on_completed) 39 | 40 | return AnonymousObservable(subscribe) 41 | -------------------------------------------------------------------------------- /rx/linq/observable/selectswitch.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable, alias=["flat_map_latest", "switch_map"]) 6 | def select_switch(self, selector): 7 | """Projects each element of an observable sequence into a new sequence 8 | of observable sequences by incorporating the element's index and then 9 | transforms an observable sequence of observable sequences into an 10 | observable sequence producing values only from the most recent 11 | observable sequence. 12 | 13 | Keyword arguments: 14 | selector -- {Function} A transform function to apply to each source 15 | element; the second parameter of the function represents the index 16 | of the source element. 17 | 18 | Returns an observable {Observable} sequence whose elements are the 19 | result of invoking the transform function on each element of source 20 | producing an Observable of Observable sequences and that at any point in 21 | time produces the elements of the most recent inner observable sequence 22 | that has been received. 23 | """ 24 | 25 | return self.map(selector).switch_latest() 26 | -------------------------------------------------------------------------------- /rx/linq/observable/single.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from .singleordefault import single_or_default_async 5 | 6 | 7 | @extensionmethod(Observable) 8 | def single(self, predicate=None): 9 | """Returns the only element of an observable sequence that satisfies the 10 | condition in the optional predicate, and reports an exception if there 11 | is not exactly one element in the observable sequence. 12 | 13 | Example: 14 | res = source.single() 15 | res = source.single(lambda x: x == 42) 16 | 17 | Keyword arguments: 18 | predicate -- {Function} [Optional] A predicate function to evaluate for 19 | elements in the source sequence. 20 | 21 | Returns {Observable} Sequence containing the single element in the 22 | observable sequence that satisfies the condition in the predicate. 23 | """ 24 | 25 | return self.filter(predicate).single() if predicate else \ 26 | single_or_default_async(self, False) 27 | -------------------------------------------------------------------------------- /rx/linq/observable/skip.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import ArgumentOutOfRangeException 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def skip(self, count): 8 | """Bypasses a specified number of elements in an observable sequence 9 | and then returns the remaining elements. 10 | 11 | Keyword arguments: 12 | count -- The number of elements to skip before returning the remaining 13 | elements. 14 | 15 | Returns an observable sequence that contains the elements that occur 16 | after the specified index in the input sequence. 17 | """ 18 | 19 | if count < 0: 20 | raise ArgumentOutOfRangeException() 21 | 22 | observable = self 23 | 24 | def subscribe(observer): 25 | remaining = [count] 26 | 27 | def on_next(value): 28 | if remaining[0] <= 0: 29 | observer.on_next(value) 30 | else: 31 | remaining[0] -= 1 32 | 33 | return observable.subscribe(on_next, observer.on_error, observer.on_completed) 34 | return AnonymousObservable(subscribe) 35 | -------------------------------------------------------------------------------- /rx/linq/observable/skiplast.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def skip_last(self, count): 7 | """Bypasses a specified number of elements at the end of an observable 8 | sequence. 9 | 10 | Description: 11 | This operator accumulates a queue with a length enough to store the 12 | first `count` elements. As more elements are received, elements are 13 | taken from the front of the queue and produced on the result sequence. 14 | This causes elements to be delayed. 15 | 16 | Keyword arguments 17 | count -- Number of elements to bypass at the end of the source sequence. 18 | 19 | Returns an observable {Observable} sequence containing the source 20 | sequence elements except for the bypassed ones at the end. 21 | """ 22 | 23 | source = self 24 | 25 | def subscribe(observer): 26 | q = [] 27 | 28 | def on_next(x): 29 | front = None 30 | with self.lock: 31 | q.append(x) 32 | if len(q) > count: 33 | front = q.pop(0) 34 | 35 | if not front is None: 36 | observer.on_next(front) 37 | 38 | return source.subscribe(on_next, observer.on_error, 39 | observer.on_completed) 40 | return AnonymousObservable(subscribe) 41 | 42 | -------------------------------------------------------------------------------- /rx/linq/observable/skiplastwithtime.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def skip_last_with_time(self, duration, scheduler=None): 8 | """Skips elements for the specified duration from the end of the 9 | observable source sequence, using the specified scheduler to run timers. 10 | 11 | 1 - res = source.skip_last_with_time(5000) 12 | 2 - res = source.skip_last_with_time(5000, scheduler) 13 | 14 | Description: 15 | This operator accumulates a queue with a length enough to store elements 16 | received during the initial duration window. As more elements are 17 | received, elements older than the specified duration are taken from the 18 | queue and produced on the result sequence. This causes elements to be 19 | delayed with duration. 20 | 21 | Keyword arguments: 22 | duration -- {Number} Duration for skipping elements from the end of the 23 | sequence. 24 | scheduler -- {Scheduler} [Optional] Scheduler to run the timer on. If 25 | not specified, defaults to Rx.Scheduler.timeout 26 | Returns an observable {Observable} sequence with the elements skipped 27 | during the specified duration from the end of the source sequence. 28 | """ 29 | 30 | scheduler = scheduler or timeout_scheduler 31 | duration = scheduler.to_timedelta(duration) 32 | source = self 33 | 34 | def subscribe(observer): 35 | q = [] 36 | 37 | def on_next(x): 38 | now = scheduler.now 39 | q.append({"interval": now, "value": x}) 40 | while len(q) and now - q[0]["interval"] >= duration: 41 | observer.on_next(q.pop(0)["value"]) 42 | 43 | def on_completed(): 44 | now = scheduler.now 45 | while len(q) and now - q[0]["interval"] >= duration: 46 | observer.on_next(q.pop(0)["value"]) 47 | 48 | observer.on_completed() 49 | 50 | return source.subscribe(on_next, observer.on_error, on_completed) 51 | return AnonymousObservable(subscribe) 52 | -------------------------------------------------------------------------------- /rx/linq/observable/skipuntil.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.disposables import CompositeDisposable, SingleAssignmentDisposable 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def skip_until(self, other): 8 | """Returns the values from the source observable sequence only after 9 | the other observable sequence produces a value. 10 | 11 | other -- The observable sequence that triggers propagation of elements 12 | of the source sequence. 13 | 14 | Returns an observable sequence containing the elements of the source 15 | sequence starting from the point the other sequence triggered 16 | propagation. 17 | """ 18 | 19 | source = self 20 | other = Observable.from_future(other) 21 | 22 | def subscribe(observer): 23 | is_open = [False] 24 | 25 | def on_next(left): 26 | if is_open[0]: 27 | observer.on_next(left) 28 | 29 | def on_completed(): 30 | if is_open[0]: 31 | observer.on_completed() 32 | 33 | subs = source.subscribe(on_next, observer.on_error, on_completed) 34 | disposables = CompositeDisposable(subs) 35 | 36 | right_subscription = SingleAssignmentDisposable() 37 | disposables.add(right_subscription) 38 | 39 | def on_next2(x): 40 | is_open[0] = True 41 | right_subscription.dispose() 42 | 43 | def on_completed2(): 44 | right_subscription.dispose() 45 | 46 | right_subscription.disposable = other.subscribe(on_next2, observer.on_error, on_completed2) 47 | 48 | return disposables 49 | return AnonymousObservable(subscribe) 50 | -------------------------------------------------------------------------------- /rx/linq/observable/skipuntilwithtime.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from rx.core import Observable, AnonymousObservable 4 | from rx.disposables import CompositeDisposable 5 | from rx.internal import extensionmethod 6 | from rx.concurrency import timeout_scheduler 7 | 8 | 9 | @extensionmethod(Observable) 10 | def skip_until_with_time(self, start_time, scheduler=None): 11 | """Skips elements from the observable source sequence until the 12 | specified start time, using the specified scheduler to run timers. 13 | Errors produced by the source sequence are always forwarded to the 14 | result sequence, even if the error occurs before the start time. 15 | 16 | Examples: 17 | res = source.skip_until_with_time(new Date(), [optional scheduler]); 18 | res = source.skip_until_with_time(5000, [optional scheduler]); 19 | 20 | Keyword arguments: 21 | start_time -- Time to start taking elements from the source sequence. If 22 | this value is less than or equal to Date(), no elements will be 23 | skipped. 24 | scheduler -- Scheduler to run the timer on. If not specified, defaults 25 | to rx.Scheduler.timeout. 26 | 27 | Returns {Observable} An observable sequence with the elements skipped 28 | until the specified start time. 29 | """ 30 | 31 | scheduler = scheduler or timeout_scheduler 32 | 33 | source = self 34 | 35 | if isinstance(start_time, datetime): 36 | scheduler_method = 'schedule_absolute' 37 | else: 38 | scheduler_method = 'schedule_relative' 39 | 40 | def subscribe(observer): 41 | open = [False] 42 | 43 | def on_next(x): 44 | if open[0]: 45 | observer.on_next(x) 46 | subscription = source.subscribe(on_next, observer.on_error, 47 | observer.on_completed) 48 | 49 | def action(scheduler, state): 50 | open[0] = True 51 | disposable = getattr(scheduler, scheduler_method)(start_time, action) 52 | return CompositeDisposable(disposable, subscription) 53 | return AnonymousObservable(subscribe) 54 | -------------------------------------------------------------------------------- /rx/linq/observable/skipwhile.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.utils import adapt_call 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def skip_while(self, predicate): 8 | """Bypasses elements in an observable sequence as long as a specified 9 | condition is true and then returns the remaining elements. The 10 | element's index is used in the logic of the predicate function. 11 | 12 | 1 - source.skip_while(lambda value: value < 10) 13 | 2 - source.skip_while(lambda value, index: value < 10 or index < 10) 14 | 15 | predicate -- A function to test each element for a condition; the 16 | second parameter of the function represents the index of the 17 | source element. 18 | 19 | Returns an observable sequence that contains the elements from the 20 | input sequence starting at the first element in the linear series that 21 | does not pass the test specified by predicate. 22 | """ 23 | 24 | predicate = adapt_call(predicate) 25 | source = self 26 | 27 | def subscribe(observer): 28 | i, running = [0], [False] 29 | 30 | def on_next(value): 31 | if not running[0]: 32 | try: 33 | running[0] = not predicate(value, i[0]) 34 | except Exception as exn: 35 | observer.on_error(exn) 36 | return 37 | else: 38 | i[0] += 1 39 | 40 | if running[0]: 41 | observer.on_next(value) 42 | 43 | return source.subscribe(on_next, observer.on_error, observer.on_completed) 44 | return AnonymousObservable(subscribe) 45 | -------------------------------------------------------------------------------- /rx/linq/observable/skipwithtime.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.disposables import CompositeDisposable 3 | from rx.internal import extensionmethod 4 | from rx.concurrency import timeout_scheduler 5 | 6 | @extensionmethod(Observable) 7 | def skip_with_time(self, duration, scheduler=None): 8 | """Skips elements for the specified duration from the start of the 9 | observable source sequence, using the specified scheduler to run timers. 10 | 11 | Example: 12 | 1 - res = source.skip_with_time(5000, [optional scheduler]) 13 | 14 | Description: 15 | Specifying a zero value for duration doesn't guarantee no elements will 16 | be dropped from the start of the source sequence. This is a side-effect 17 | of the asynchrony introduced by the scheduler, where the action that 18 | causes callbacks from the source sequence to be forwarded may not 19 | execute immediately, despite the zero due time. 20 | 21 | Errors produced by the source sequence are always forwarded to the 22 | result sequence, even if the error occurs before the duration. 23 | 24 | Keyword arguments: 25 | duration -- {Number} Duration for skipping elements from the start of 26 | the sequence. 27 | scheduler -- {Scheduler} Scheduler to run the timer on. If not 28 | specified, defaults to Rx.Scheduler.timeout. 29 | 30 | Returns n observable {Observable} sequence with the elements skipped 31 | during the specified duration from the start of the source sequence. 32 | """ 33 | 34 | source = self 35 | scheduler = scheduler or timeout_scheduler 36 | 37 | def subscribe(observer): 38 | open = [False] 39 | 40 | def action(scheduler, state): 41 | open[0] = True 42 | 43 | t = scheduler.schedule_relative(duration, action) 44 | 45 | def on_next(x): 46 | if open[0]: 47 | observer.on_next(x) 48 | 49 | d = source.subscribe(on_next, observer.on_error, observer.on_completed) 50 | return CompositeDisposable(t, d) 51 | return AnonymousObservable(subscribe) 52 | 53 | -------------------------------------------------------------------------------- /rx/linq/observable/some.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def some(self, predicate=None): 7 | """Determines whether some element of an observable sequence satisfies a 8 | condition if present, else if some items are in the sequence. 9 | 10 | Example: 11 | result = source.some() 12 | result = source.some(lambda x: x > 3) 13 | 14 | Keyword arguments: 15 | predicate -- A function to test each element for a condition. 16 | 17 | Returns {Observable} an observable sequence containing a single element 18 | determining whether some elements in the source sequence pass the test 19 | in the specified predicate if given, else if some items are in the 20 | sequence. 21 | """ 22 | 23 | source = self 24 | def subscribe(observer): 25 | def on_next(_): 26 | observer.on_next(True) 27 | observer.on_completed() 28 | def on_error(): 29 | observer.on_next(False) 30 | observer.on_completed() 31 | return source.subscribe(on_next, observer.on_error, on_error) 32 | 33 | return source.filter(predicate).some() if predicate else AnonymousObservable(subscribe) 34 | -------------------------------------------------------------------------------- /rx/linq/observable/start.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def start(cls, func, scheduler=None): 7 | """Invokes the specified function asynchronously on the specified 8 | scheduler, surfacing the result through an observable sequence. 9 | 10 | Example: 11 | res = rx.Observable.start(lambda: pprint('hello')) 12 | res = rx.Observable.start(lambda: pprint('hello'), rx.Scheduler.timeout) 13 | 14 | Keyword arguments: 15 | func -- {Function} Function to run asynchronously. 16 | scheduler -- {Scheduler} [Optional] Scheduler to run the function on. If 17 | not specified, defaults to Scheduler.timeout. 18 | 19 | Returns {Observable} An observable sequence exposing the function's 20 | result value, or an exception. 21 | 22 | Remarks: 23 | The function is called immediately, not during the subscription of the 24 | resulting sequence. Multiple subscriptions to the resulting sequence can 25 | observe the function's result. 26 | """ 27 | 28 | return Observable.to_async(func, scheduler)() 29 | -------------------------------------------------------------------------------- /rx/linq/observable/startasync.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable) 6 | def start_async(cls, function_async): 7 | """Invokes the asynchronous function, surfacing the result through an 8 | observable sequence. 9 | 10 | Keyword arguments: 11 | :param types.FunctionType function_async: Asynchronous function which 12 | returns a Future to run. 13 | 14 | :returns: An observable sequence exposing the function's result value, or an 15 | exception. 16 | :rtype: Observable 17 | """ 18 | 19 | try: 20 | future = function_async() 21 | except Exception as ex: 22 | return Observable.throw(ex) 23 | 24 | return Observable.from_future(future) 25 | -------------------------------------------------------------------------------- /rx/linq/observable/startswith.py: -------------------------------------------------------------------------------- 1 | from rx.core import Scheduler, Observable 2 | 3 | from rx.concurrency import immediate_scheduler 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def start_with(self, *args, **kw): 9 | """Prepends a sequence of values to an observable sequence with an 10 | optional scheduler and an argument list of values to prepend. 11 | 12 | 1 - source.start_with(1, 2, 3) 13 | 2 - source.start_with(Scheduler.timeout, 1, 2, 3) 14 | 15 | Returns the source sequence prepended with the specified values. 16 | """ 17 | 18 | if isinstance(args[0], Scheduler): 19 | scheduler = args[0] 20 | args = args[1:] 21 | else: 22 | scheduler = kw.get("scheduler", immediate_scheduler) 23 | 24 | sequence = [Observable.from_(args, scheduler), self] 25 | return Observable.concat(sequence) 26 | -------------------------------------------------------------------------------- /rx/linq/observable/subscribeon.py: -------------------------------------------------------------------------------- 1 | from rx import AnonymousObservable, Observable 2 | from rx.disposables import SingleAssignmentDisposable, SerialDisposable, ScheduledDisposable 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def subscribe_on(self, scheduler): 8 | """Subscribe on the specified scheduler. 9 | 10 | Wrap the source sequence in order to run its subscription and 11 | unsubscription logic on the specified scheduler. This operation is not 12 | commonly used; see the remarks section for more information on the 13 | distinction between subscribe_on and observe_on. 14 | 15 | Keyword arguments: 16 | scheduler -- Scheduler to perform subscription and unsubscription 17 | actions on. 18 | 19 | Returns the source sequence whose subscriptions and unsubscriptions 20 | happen on the specified scheduler. 21 | 22 | This only performs the side-effects of subscription and unsubscription 23 | on the specified scheduler. In order to invoke observer callbacks on a 24 | scheduler, use observe_on. 25 | """ 26 | source = self 27 | 28 | def subscribe(observer): 29 | m = SingleAssignmentDisposable() 30 | d = SerialDisposable() 31 | d.disposable = m 32 | 33 | def action(scheduler, state): 34 | d.disposable = ScheduledDisposable(scheduler, source.subscribe(observer)) 35 | 36 | m.disposable = scheduler.schedule(action) 37 | return d 38 | 39 | return AnonymousObservable(subscribe) 40 | -------------------------------------------------------------------------------- /rx/linq/observable/sum.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def sum(self, key_selector=None): 7 | """Computes the sum of a sequence of values that are obtained by 8 | invoking an optional transform function on each element of the input 9 | sequence, else if not specified computes the sum on each item in the 10 | sequence. 11 | 12 | Example 13 | res = source.sum() 14 | res = source.sum(lambda x: x.value) 15 | 16 | key_selector -- {Function} [Optional] A transform function to apply to 17 | each element. 18 | 19 | Returns an observable {Observable} sequence containing a single element 20 | with the sum of the values in the source sequence. 21 | """ 22 | 23 | if key_selector: 24 | return self.map(key_selector).sum() 25 | else: 26 | return self.reduce(seed=0, accumulator=lambda prev, curr: prev + curr) 27 | -------------------------------------------------------------------------------- /rx/linq/observable/take.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import ArgumentOutOfRangeException 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def take(self, count, scheduler=None): 8 | """Returns a specified number of contiguous elements from the start of 9 | an observable sequence, using the specified scheduler for the edge case 10 | of take(0). 11 | 12 | 1 - source.take(5) 13 | 2 - source.take(0, rx.Scheduler.timeout) 14 | 15 | Keyword arguments: 16 | count -- The number of elements to return. 17 | scheduler -- [Optional] Scheduler used to produce an OnCompleted 18 | message in case count is set to 0. 19 | 20 | Returns an observable sequence that contains the specified number of 21 | elements from the start of the input sequence. 22 | """ 23 | 24 | if count < 0: 25 | raise ArgumentOutOfRangeException() 26 | 27 | if not count: 28 | return Observable.empty(scheduler) 29 | 30 | observable = self 31 | def subscribe(observer): 32 | remaining = [count] 33 | 34 | def on_next(value): 35 | if remaining[0] > 0: 36 | remaining[0] -= 1 37 | observer.on_next(value) 38 | if not remaining[0]: 39 | observer.on_completed() 40 | 41 | return observable.subscribe(on_next, observer.on_error, observer.on_completed) 42 | return AnonymousObservable(subscribe) 43 | -------------------------------------------------------------------------------- /rx/linq/observable/takelast.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def take_last(self, count): 7 | """Returns a specified number of contiguous elements from the end of an 8 | observable sequence. 9 | 10 | Example: 11 | res = source.take_last(5) 12 | 13 | Description: 14 | This operator accumulates a buffer with a length enough to store 15 | elements count elements. Upon completion of the source sequence, this 16 | buffer is drained on the result sequence. This causes the elements to be 17 | delayed. 18 | 19 | Keyword arguments: 20 | :param int count: Number of elements to take from the end of the source 21 | sequence. 22 | 23 | :returns: An observable sequence containing the specified number of elements 24 | from the end of the source sequence. 25 | :rtype: Observable 26 | """ 27 | 28 | source = self 29 | 30 | def subscribe(observer): 31 | q = [] 32 | def on_next(x): 33 | q.append(x) 34 | if len(q) > count: 35 | q.pop(0) 36 | 37 | def on_completed(): 38 | while len(q): 39 | observer.on_next(q.pop(0)) 40 | observer.on_completed() 41 | 42 | return source.subscribe(on_next, observer.on_error, on_completed) 43 | return AnonymousObservable(subscribe) 44 | -------------------------------------------------------------------------------- /rx/linq/observable/takelastbuffer.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def take_last_buffer(self, count): 7 | """Returns an array with the specified number of contiguous elements 8 | from the end of an observable sequence. 9 | 10 | Example: 11 | res = source.take_last(5) 12 | 13 | Description: 14 | This operator accumulates a buffer with a length enough to store 15 | elements count elements. Upon completion of the source sequence, this 16 | buffer is drained on the result sequence. This causes the elements to be 17 | delayed. 18 | 19 | Keyword arguments: 20 | :param int count: Number of elements to take from the end of the source 21 | sequence. 22 | 23 | :returns: An observable sequence containing a single list with the specified 24 | number of elements from the end of the source sequence. 25 | :rtype: Observable 26 | """ 27 | 28 | source = self 29 | 30 | def subscribe(observer): 31 | q = [] 32 | def on_next(x): 33 | with self.lock: 34 | q.append(x) 35 | if len(q) > count: 36 | q.pop(0) 37 | 38 | def on_completed(): 39 | observer.on_next(q) 40 | observer.on_completed() 41 | 42 | return source.subscribe(on_next, observer.on_error, on_completed) 43 | return AnonymousObservable(subscribe) 44 | -------------------------------------------------------------------------------- /rx/linq/observable/takelastwithtime.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | from rx.concurrency import timeout_scheduler 4 | 5 | 6 | @extensionmethod(Observable) 7 | def take_last_with_time(self, duration, scheduler=None): 8 | """Returns elements within the specified duration from the end of the 9 | observable source sequence, using the specified schedulers to run timers 10 | and to drain the collected elements. 11 | 12 | Example: 13 | res = source.take_last_with_time(5000, scheduler) 14 | 15 | Description: 16 | This operator accumulates a queue with a length enough to store elements 17 | received during the initial duration window. As more elements are 18 | received, elements older than the specified duration are taken from the 19 | queue and produced on the result sequence. This causes elements to be 20 | delayed with duration. 21 | 22 | Keyword arguments: 23 | duration -- {Number} Duration for taking elements from the end of the 24 | sequence. 25 | scheduler -- {Scheduler} [Optional] Scheduler to run the timer on. If 26 | not specified, defaults to rx.Scheduler.timeout. 27 | 28 | Returns {Observable} An observable sequence with the elements taken 29 | during the specified duration from the end of the source sequence. 30 | """ 31 | 32 | source = self 33 | scheduler = scheduler or timeout_scheduler 34 | duration = scheduler.to_timedelta(duration) 35 | 36 | def subscribe(observer): 37 | q = [] 38 | 39 | def on_next(x): 40 | now = scheduler.now 41 | q.append({"interval": now, "value": x}) 42 | while len(q) and now - q[0]["interval"] >= duration: 43 | q.pop(0) 44 | 45 | def on_completed(): 46 | now = scheduler.now 47 | while len(q): 48 | next = q.pop(0) 49 | if now - next["interval"] <= duration: 50 | observer.on_next(next["value"]) 51 | 52 | observer.on_completed() 53 | 54 | return source.subscribe(on_next, observer.on_error, on_completed) 55 | return AnonymousObservable(subscribe) 56 | -------------------------------------------------------------------------------- /rx/linq/observable/takeuntil.py: -------------------------------------------------------------------------------- 1 | from rx.internal import noop 2 | from rx.core import Observable, AnonymousObservable 3 | from rx.disposables import CompositeDisposable 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def take_until(self, other): 9 | """Returns the values from the source observable sequence until the 10 | other observable sequence produces a value. 11 | 12 | Keyword arguments: 13 | other -- Observable sequence that terminates propagation of elements of 14 | the source sequence. 15 | 16 | Returns an observable sequence containing the elements of the source 17 | sequence up to the point the other sequence interrupted further 18 | propagation. 19 | """ 20 | 21 | source = self 22 | other = Observable.from_future(other) 23 | 24 | def subscribe(observer): 25 | 26 | def on_completed(x): 27 | observer.on_completed() 28 | 29 | return CompositeDisposable( 30 | source.subscribe(observer), 31 | other.subscribe(on_completed, observer.on_error, noop) 32 | ) 33 | return AnonymousObservable(subscribe) 34 | 35 | 36 | -------------------------------------------------------------------------------- /rx/linq/observable/takeuntilwithtime.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from rx.core import Observable, AnonymousObservable 4 | from rx.disposables import CompositeDisposable 5 | from rx.internal import extensionmethod 6 | from rx.concurrency import timeout_scheduler 7 | 8 | @extensionmethod(Observable) 9 | def take_until_with_time(self, end_time, scheduler=None): 10 | """Takes elements for the specified duration until the specified end 11 | time, using the specified scheduler to run timers. 12 | 13 | Examples: 14 | 1 - res = source.take_until_with_time(dt, [optional scheduler]) 15 | 2 - res = source.take_until_with_time(5000, [optional scheduler]) 16 | 17 | Keyword Arguments: 18 | end_time -- {Number | Date} Time to stop taking elements from the source 19 | sequence. If this value is less than or equal to Date(), the 20 | result stream will complete immediately. 21 | scheduler -- {Scheduler} Scheduler to run the timer on. 22 | 23 | Returns an observable {Observable} sequence with the elements taken 24 | until the specified end time. 25 | """ 26 | 27 | scheduler = scheduler or timeout_scheduler 28 | source = self 29 | 30 | if isinstance(end_time, datetime): 31 | scheduler_method = scheduler.schedule_absolute 32 | else: 33 | scheduler_method = scheduler.schedule_relative 34 | 35 | def subscribe(observer): 36 | def action(scheduler, state): 37 | observer.on_completed() 38 | 39 | task = scheduler_method(end_time, action) 40 | return CompositeDisposable(task, source.subscribe(observer)) 41 | return AnonymousObservable(subscribe) 42 | 43 | -------------------------------------------------------------------------------- /rx/linq/observable/takewhile.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.utils import adapt_call 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def take_while(self, predicate): 8 | """Returns elements from an observable sequence as long as a specified 9 | condition is true. The element's index is used in the logic of the 10 | predicate function. 11 | 12 | 1 - source.take_while(lambda value: value < 10) 13 | 2 - source.take_while(lambda value, index: value < 10 or index < 10) 14 | 15 | Keyword arguments: 16 | predicate -- A function to test each element for a condition; the 17 | second parameter of the function represents the index of the source 18 | element. 19 | 20 | Returns an observable sequence that contains the elements from the 21 | input sequence that occur before the element at which the test no 22 | longer passes. 23 | """ 24 | 25 | predicate = adapt_call(predicate) 26 | observable = self 27 | def subscribe(observer): 28 | running, i = [True], [0] 29 | 30 | def on_next(value): 31 | with self.lock: 32 | if not running[0]: 33 | return 34 | 35 | try: 36 | running[0] = predicate(value, i[0]) 37 | except Exception as exn: 38 | observer.on_error(exn) 39 | return 40 | else: 41 | i[0] += 1 42 | 43 | if running[0]: 44 | observer.on_next(value) 45 | else: 46 | observer.on_completed() 47 | 48 | return observable.subscribe(on_next, observer.on_error, observer.on_completed) 49 | return AnonymousObservable(subscribe) 50 | 51 | -------------------------------------------------------------------------------- /rx/linq/observable/takewithtime.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.disposables import CompositeDisposable 3 | from rx.concurrency import timeout_scheduler 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def take_with_time(self, duration, scheduler=None): 9 | """Takes elements for the specified duration from the start of the 10 | observable source sequence, using the specified scheduler to run timers. 11 | 12 | Example: 13 | res = source.take_with_time(5000, [optional scheduler]) 14 | 15 | Description: 16 | This operator accumulates a queue with a length enough to store elements 17 | received during the initial duration window. As more elements are 18 | received, elements older than the specified duration are taken from the 19 | queue and produced on the result sequence. This causes elements to be 20 | delayed with duration. 21 | 22 | Keyword arguments: 23 | duration -- {Number} Duration for taking elements from the start of the 24 | sequence. 25 | scheduler -- {Scheduler} Scheduler to run the timer on. If not 26 | specified, defaults to rx.Scheduler.timeout. 27 | 28 | Returns {Observable} An observable sequence with the elements taken 29 | during the specified duration from the start of the source sequence. 30 | """ 31 | 32 | source = self 33 | scheduler = scheduler or timeout_scheduler 34 | 35 | def subscribe(observer): 36 | def action(scheduler, state): 37 | observer.on_completed() 38 | disposable = scheduler.schedule_relative(duration, action) 39 | return CompositeDisposable(disposable, source.subscribe(observer)) 40 | return AnonymousObservable(subscribe) 41 | -------------------------------------------------------------------------------- /rx/linq/observable/thendo.py: -------------------------------------------------------------------------------- 1 | from rx import Observable 2 | from rx.internal import extensionmethod 3 | 4 | from rx.joins import Pattern 5 | 6 | @extensionmethod(Observable, alias="then") 7 | def then_do(self, selector): 8 | """Matches when the observable sequence has an available value and projects 9 | the value. 10 | 11 | :param types.FunctionType selector: Selector that will be invoked for values 12 | in the source sequence. 13 | :returns: Plan that produces the projected values, to be fed (with other 14 | plans) to the when operator. 15 | :rtype: Plan 16 | """ 17 | 18 | return Pattern([self]).then_do(selector) 19 | 20 | -------------------------------------------------------------------------------- /rx/linq/observable/throttlefirst.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def throttle_first(self, window_duration, scheduler=None): 8 | """Returns an Observable that emits only the first item emitted by the 9 | source Observable during sequential time windows of a specified 10 | duration. 11 | 12 | Keyword arguments: 13 | window_duration -- {timedelta} time to wait before emitting another item 14 | after emitting the last item. 15 | scheduler -- {Scheduler} [Optional] the Scheduler to use internally to 16 | manage the timers that handle timeout for each item. If not 17 | provided, defaults to Scheduler.timeout. 18 | Returns {Observable} An Observable that performs the throttle operation. 19 | """ 20 | 21 | scheduler = scheduler or timeout_scheduler 22 | duration = scheduler.to_timedelta(+window_duration or 0) 23 | if duration <= scheduler.to_timedelta(0): 24 | raise ValueError('window_duration cannot be less or equal zero.') 25 | 26 | source = self 27 | 28 | def subscribe(observer): 29 | last_on_next = [0] 30 | 31 | def on_next(x): 32 | emit = False 33 | now = scheduler.now 34 | 35 | with self.lock: 36 | if not last_on_next[0] or now - last_on_next[0] >= duration: 37 | last_on_next[0] = now 38 | emit = True 39 | if emit: 40 | observer.on_next(x) 41 | 42 | return source.subscribe(on_next, observer.on_error, observer.on_completed) 43 | return AnonymousObservable(subscribe) 44 | -------------------------------------------------------------------------------- /rx/linq/observable/throw.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | 3 | from rx.concurrency import immediate_scheduler 4 | from rx.internal import extensionclassmethod 5 | 6 | 7 | @extensionclassmethod(Observable, alias="throw_exception") 8 | def throw(cls, exception, scheduler=None): 9 | """Returns an observable sequence that terminates with an exception, 10 | using the specified scheduler to send out the single OnError message. 11 | 12 | 1 - res = rx.Observable.throw_exception(Exception('Error')) 13 | 2 - res = rx.Observable.throw_exception(Exception('Error'), 14 | rx.Scheduler.timeout) 15 | 16 | Keyword arguments: 17 | exception -- An object used for the sequence's termination. 18 | scheduler -- Scheduler to send the exceptional termination call on. If 19 | not specified, defaults to ImmediateScheduler. 20 | 21 | Returns the observable sequence that terminates exceptionally with the 22 | specified exception object. 23 | """ 24 | 25 | scheduler = scheduler or immediate_scheduler 26 | 27 | exception = exception if isinstance(exception, Exception) else Exception(exception) 28 | 29 | def subscribe(observer): 30 | def action(scheduler, state): 31 | observer.on_error(exception) 32 | 33 | return scheduler.schedule(action) 34 | return AnonymousObservable(subscribe) 35 | -------------------------------------------------------------------------------- /rx/linq/observable/timeinterval.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.internal.utils import TimeInterval 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def time_interval(self, scheduler=None): 9 | """Records the time interval between consecutive values in an 10 | observable sequence. 11 | 12 | 1 - res = source.time_interval(); 13 | 2 - res = source.time_interval(Scheduler.timeout) 14 | 15 | Keyword arguments: 16 | scheduler -- [Optional] Scheduler used to compute time intervals. If 17 | not specified, the timeout scheduler is used. 18 | 19 | Return An observable sequence with time interval information on values. 20 | """ 21 | 22 | source = self 23 | scheduler = scheduler or timeout_scheduler 24 | 25 | def defer(): 26 | last = [scheduler.now] 27 | 28 | def selector(x): 29 | now = scheduler.now 30 | span = now - last[0] 31 | last[0] = now 32 | return TimeInterval(value=x, interval=span) 33 | 34 | return source.map(selector) 35 | return Observable.defer(defer) 36 | -------------------------------------------------------------------------------- /rx/linq/observable/timestamp.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from rx.core import Observable 4 | from rx.concurrency import timeout_scheduler 5 | from rx.internal.utils import Timestamp 6 | from rx.internal import extensionmethod 7 | 8 | log = logging.getLogger("Rx") 9 | 10 | 11 | @extensionmethod(Observable) 12 | def timestamp(self, scheduler=None): 13 | """Records the timestamp for each value in an observable sequence. 14 | 15 | 1 - res = source.timestamp() # produces objects with attributes "value" and 16 | "timestamp", where value is the original value. 17 | 2 - res = source.timestamp(Scheduler.timeout) 18 | 19 | :param Scheduler scheduler: [Optional] Scheduler used to compute timestamps. If not 20 | specified, the timeout scheduler is used. 21 | 22 | Returns an observable sequence with timestamp information on values. 23 | """ 24 | 25 | scheduler = scheduler or timeout_scheduler 26 | 27 | def selector(x): 28 | return Timestamp(value=x, timestamp=scheduler.now) 29 | 30 | return self.map(selector) 31 | -------------------------------------------------------------------------------- /rx/linq/observable/toasync.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.concurrency import timeout_scheduler 3 | from rx.subjects import AsyncSubject 4 | from rx.internal import extensionclassmethod 5 | 6 | 7 | @extensionclassmethod(Observable) 8 | def to_async(cls, func, scheduler=None): 9 | """Converts the function into an asynchronous function. Each invocation 10 | of the resulting asynchronous function causes an invocation of the 11 | original synchronous function on the specified scheduler. 12 | 13 | Example: 14 | res = Observable.to_async(lambda x, y: x + y)(4, 3) 15 | res = Observable.to_async(lambda x, y: x + y, Scheduler.timeout)(4, 3) 16 | res = Observable.to_async(lambda x: log.debug(x), 17 | Scheduler.timeout)('hello') 18 | 19 | func -- {Function} Function to convert to an asynchronous function. 20 | scheduler -- {Scheduler} [Optional] Scheduler to run the function on. If 21 | not specified, defaults to Scheduler.timeout. 22 | 23 | Returns {Function} Asynchronous function. 24 | """ 25 | 26 | scheduler = scheduler or timeout_scheduler 27 | 28 | def wrapper(*args): 29 | subject = AsyncSubject() 30 | 31 | def action(scheduler, state): 32 | try: 33 | result = func(*args) 34 | except Exception as ex: 35 | subject.on_error(ex) 36 | return 37 | 38 | subject.on_next(result) 39 | subject.on_completed() 40 | 41 | scheduler.schedule(action) 42 | return subject.as_observable() 43 | return wrapper 44 | -------------------------------------------------------------------------------- /rx/linq/observable/toblocking.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.core.blockingobservable import BlockingObservable 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def to_blocking(self): 8 | return BlockingObservable(self) 9 | -------------------------------------------------------------------------------- /rx/linq/observable/todict.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | def _to_dict(source, map_type, key_selector, element_selector): 5 | def subscribe(observer): 6 | m = map_type() 7 | 8 | def on_next(x): 9 | try: 10 | key = key_selector(x) 11 | except Exception as ex: 12 | observer.on_error(ex) 13 | return 14 | 15 | element = x 16 | if element_selector: 17 | try: 18 | element = element_selector(x) 19 | except Exception as ex: 20 | observer.on_error(ex) 21 | return 22 | 23 | m[key] = element 24 | 25 | def on_completed(): 26 | observer.on_next(m) 27 | observer.on_completed() 28 | 29 | return source.subscribe(on_next, observer.on_error, on_completed) 30 | return AnonymousObservable(subscribe) 31 | 32 | 33 | @extensionmethod(Observable) 34 | def to_dict(self, key_selector, element_selector=None): 35 | """Converts the observable sequence to a Map if it exists. 36 | 37 | Keyword arguments: 38 | key_selector -- {Function} A function which produces the key for the 39 | Map. 40 | element_selector -- {Function} [Optional] An optional function which 41 | produces the element for the Map. If not present, defaults to the 42 | value from the observable sequence. 43 | Returns {Observable} An observable sequence with a single value of a Map 44 | containing the values from the observable sequence. 45 | """ 46 | 47 | return _to_dict(self, dict, key_selector, element_selector) 48 | 49 | -------------------------------------------------------------------------------- /rx/linq/observable/tofuture.py: -------------------------------------------------------------------------------- 1 | import rx 2 | from rx.core import Observable 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable) 7 | def to_future(self, future_ctor=None): 8 | """Converts an existing observable sequence to a Future 9 | 10 | Example: 11 | future = rx.Observable.return_value(42).to_future(trollius.Future); 12 | 13 | With config: 14 | rx.config["Future"] = trollius.Future 15 | future = rx.Observable.return_value(42).to_future() 16 | 17 | future_ctor -- {Function} [Optional] The constructor of the future. 18 | If not provided, it looks for it in rx.config.Future. 19 | 20 | Returns {Future} An future with the last value from the observable 21 | sequence. 22 | """ 23 | 24 | future_ctor = future_ctor or rx.config.get("Future") 25 | if not future_ctor: 26 | raise Exception('Future type not provided nor in rx.config.Future') 27 | 28 | source = self 29 | 30 | future = future_ctor() 31 | 32 | value = [None] 33 | has_value = [False] 34 | 35 | def on_next(v): 36 | value[0] = v 37 | has_value[0] = True 38 | 39 | def on_error(err): 40 | future.set_exception(err) 41 | 42 | def on_completed(): 43 | if has_value[0]: 44 | future.set_result(value[0]) 45 | 46 | source.subscribe(on_next, on_error, on_completed) 47 | 48 | # No cancellation can be done 49 | return future 50 | 51 | 52 | @extensionmethod(Observable) 53 | def __await__(self): 54 | """Awaits the given observable 55 | :returns: The last item of the observable sequence. 56 | :rtype: Any 57 | :raises TypeError: If key is not of type int or slice 58 | """ 59 | return iter(self.to_future()) 60 | 61 | -------------------------------------------------------------------------------- /rx/linq/observable/toiterable.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable) 6 | def to_iterable(self): 7 | """Creates an iterable from an observable sequence. 8 | 9 | :returns: An observable sequence containing a single element with a list 10 | containing all the elements of the source sequence. 11 | :rtype: Observable 12 | """ 13 | 14 | source = self 15 | 16 | def subscribe(observer): 17 | queue = [] 18 | 19 | def on_next(item): 20 | queue.append(item) 21 | 22 | def on_completed(): 23 | observer.on_next(queue) 24 | observer.on_completed() 25 | 26 | return source.subscribe(on_next, observer.on_error, on_completed) 27 | return AnonymousObservable(subscribe) 28 | -------------------------------------------------------------------------------- /rx/linq/observable/tolist.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionmethod 3 | 4 | 5 | @extensionmethod(Observable, alias="to_iterable") 6 | def to_list(self): 7 | """Creates a list from an observable sequence. 8 | 9 | Returns an observable sequence containing a single element with a list 10 | containing all the elements of the source sequence.""" 11 | 12 | def accumulator(res, i): 13 | res = res[:] 14 | res.append(i) 15 | return res 16 | 17 | return self.scan(accumulator, seed=[]).start_with([]).last() 18 | 19 | 20 | @extensionmethod(Observable) 21 | def to_sorted_list(self, key_selector=None, reverse=False): 22 | """ 23 | Creates a sorted list from an observable sequence, 24 | with an optional key_selector used to map the attribute for sorting 25 | 26 | Returns an observable sequence containing a single element with a list 27 | containing all the sorted elements of the source sequence.""" 28 | 29 | if key_selector: 30 | return self.to_list().do_action(on_next=lambda l: l.sort(key=key_selector, reverse=reverse)) 31 | else: 32 | return self.to_list().do_action(lambda l: l.sort(reverse=reverse)) 33 | -------------------------------------------------------------------------------- /rx/linq/observable/toset.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal import extensionmethod 3 | 4 | def _to_set(source, set_type): 5 | def subscribe(observer): 6 | s = set_type() 7 | 8 | def on_completed(): 9 | observer.on_next(s) 10 | observer.on_completed() 11 | 12 | return source.subscribe(s.add, observer.on_error, on_completed) 13 | return AnonymousObservable(subscribe) 14 | 15 | 16 | @extensionmethod(Observable) 17 | def to_set(self): 18 | """Converts the observable sequence to a set. 19 | 20 | Returns {Observable} An observable sequence with a single value of a set 21 | containing the values from the observable sequence. 22 | """ 23 | 24 | return _to_set(self, set) 25 | -------------------------------------------------------------------------------- /rx/linq/observable/transduce.py: -------------------------------------------------------------------------------- 1 | """Transducers for RxPY. 2 | 3 | There are several different implementations of transducers in Python. 4 | This implementation is currently targeted for: 5 | 6 | - http://code.sixty-north.com/python-transducers 7 | 8 | You should also read the excellent article series "Understanding 9 | Transducers through Python" at: 10 | - http://sixty-north.com/blog/series/understanding-transducers-through-python 11 | 12 | Other implementations of transducers in Python are: 13 | 14 | - https://github.com/cognitect-labs/transducers-python 15 | """ 16 | 17 | from rx.core import Observable, AnonymousObservable 18 | from rx.internal import extensionmethod 19 | 20 | 21 | class Observing(object): 22 | 23 | """An observing transducer.""" 24 | 25 | def __init__(self, observer): 26 | self.observer = observer 27 | 28 | def initial(self): 29 | return self.observer 30 | 31 | def step(self, obs, input): 32 | return obs.on_next(input) 33 | 34 | def complete(self, obs): 35 | return obs.on_completed() 36 | 37 | def __call__(self, result, item): 38 | return self.step(result, item) 39 | 40 | 41 | @extensionmethod(Observable) 42 | def transduce(self, transducer): 43 | """Execute a transducer to transform the observable sequence. 44 | 45 | Keyword arguments: 46 | :param Transducer transducer: A transducer to execute. 47 | 48 | :returns: An Observable sequence containing the results from the 49 | transducer. 50 | :rtype: Observable 51 | """ 52 | source = self 53 | 54 | def subscribe(observer): 55 | xform = transducer(Observing(observer)) 56 | 57 | def on_next(v): 58 | try: 59 | xform.step(observer, v) 60 | except Exception as e: 61 | observer.on_error(e) 62 | 63 | def on_completed(): 64 | xform.complete(observer) 65 | 66 | return source.subscribe(on_next, observer.on_error, on_completed) 67 | return AnonymousObservable(subscribe) 68 | -------------------------------------------------------------------------------- /rx/linq/observable/using.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable, AnonymousObservable, Disposable 2 | from rx.disposables import CompositeDisposable 3 | from rx.internal import extensionclassmethod 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def using(cls, resource_factory, observable_factory): 8 | """Constructs an observable sequence that depends on a resource object, 9 | whose lifetime is tied to the resulting observable sequence's lifetime. 10 | 11 | 1 - res = rx.Observable.using(lambda: AsyncSubject(), lambda: s: s) 12 | 13 | Keyword arguments: 14 | resource_factory -- Factory function to obtain a resource object. 15 | observable_factory -- Factory function to obtain an observable sequence 16 | that depends on the obtained resource. 17 | 18 | Returns an observable sequence whose lifetime controls the lifetime of 19 | the dependent resource object. 20 | """ 21 | 22 | def subscribe(observer): 23 | disposable = Disposable.empty() 24 | try: 25 | resource = resource_factory() 26 | if resource: 27 | disposable = resource 28 | 29 | source = observable_factory(resource) 30 | except Exception as exception: 31 | d = Observable.throw_exception(exception).subscribe(observer) 32 | return CompositeDisposable(d, disposable) 33 | 34 | return CompositeDisposable(source.subscribe(observer), disposable) 35 | return AnonymousObservable(subscribe) 36 | -------------------------------------------------------------------------------- /rx/linq/observable/when.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObserver, AnonymousObservable 2 | from rx.disposables import CompositeDisposable 3 | from rx.internal import extensionclassmethod 4 | 5 | 6 | @extensionclassmethod(Observable) 7 | def when(cls, *args): 8 | """Joins together the results from several patterns. 9 | 10 | :param Observable cls: Observable class. 11 | :param list[Plan] args: A series of plans (specified as a list of as a 12 | series of arguments) created by use of the Then operator on patterns. 13 | :returns: Observable sequence with the results form matching several 14 | patterns. 15 | :rtype: Observable 16 | """ 17 | 18 | plans = args[0] if len(args) and isinstance(args[0], list) else list(args) 19 | 20 | def subscribe(observer): 21 | active_plans = [] 22 | external_subscriptions = {} 23 | 24 | def on_error(err): 25 | for v in external_subscriptions.values(): 26 | v.on_error(err) 27 | observer.on_error(err) 28 | 29 | out_observer = AnonymousObserver(observer.on_next, on_error, observer.on_completed) 30 | 31 | def deactivate(active_plan): 32 | active_plans.remove(active_plan) 33 | if not len(active_plans): 34 | observer.on_completed() 35 | try: 36 | for plan in plans: 37 | active_plans.append(plan.activate(external_subscriptions, 38 | out_observer, deactivate)) 39 | except Exception as ex: 40 | Observable.throw(ex).subscribe(observer) 41 | 42 | group = CompositeDisposable() 43 | for join_observer in external_subscriptions.values(): 44 | join_observer.subscribe() 45 | group.add(join_observer) 46 | 47 | return group 48 | 49 | return AnonymousObservable(subscribe) 50 | -------------------------------------------------------------------------------- /rx/linq/observable/where.py: -------------------------------------------------------------------------------- 1 | from rx import Observable, AnonymousObservable 2 | from rx.internal.utils import adapt_call 3 | from rx.internal import extensionmethod 4 | 5 | 6 | @extensionmethod(Observable, alias="filter") 7 | def where(self, predicate): 8 | """Filters the elements of an observable sequence based on a predicate 9 | by incorporating the element's index. 10 | 11 | 1 - source.filter(lambda value: value < 10) 12 | 2 - source.filter(lambda value, index: value < 10 or index < 10) 13 | 14 | Keyword arguments: 15 | :param Observable self: Observable sequence to filter. 16 | :param (T, ) -> bool predicate: A function to test each source element 17 | for a condition; the 18 | second parameter of the function represents the index of the source 19 | element. 20 | 21 | :returns: An observable sequence that contains elements from the input 22 | sequence that satisfy the condition. 23 | :rtype: Observable 24 | """ 25 | 26 | predicate = adapt_call(predicate) 27 | parent = self 28 | 29 | def subscribe(observer): 30 | count = [0] 31 | 32 | def on_next(value): 33 | try: 34 | should_run = predicate(value, count[0]) 35 | except Exception as ex: 36 | observer.on_error(ex) 37 | return 38 | else: 39 | count[0] += 1 40 | 41 | if should_run: 42 | observer.on_next(value) 43 | 44 | return parent.subscribe(on_next, observer.on_error, observer.on_completed) 45 | return AnonymousObservable(subscribe) 46 | -------------------------------------------------------------------------------- /rx/linq/observable/whiledo.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | 3 | from rx.internal.enumerable import Enumerable 4 | from rx.internal import extensionclassmethod 5 | 6 | 7 | @extensionclassmethod(Observable) 8 | def while_do(cls, condition, source): 9 | """Repeats source as long as condition holds emulating a while loop. 10 | 11 | Keyword arguments: 12 | :param types.FunctionType condition: The condition which determines if the 13 | source will be repeated. 14 | :param Observable source: The observable sequence that will be run if the 15 | condition function returns true. 16 | 17 | :returns: An observable sequence which is repeated as long as the condition 18 | holds. 19 | :rtype: Observable 20 | """ 21 | 22 | source = Observable.from_future(source) 23 | return Observable.concat(Enumerable.while_do(condition, source)) 24 | -------------------------------------------------------------------------------- /rx/linq/observable/ziparray.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observable 2 | from rx.internal import extensionclassmethod 3 | 4 | 5 | @extensionclassmethod(Observable, alias="zip_array") 6 | def zip_list(cls, *args): 7 | """Merge the specified observable sequences into one observable 8 | sequence by emitting a list with the elements of the observable 9 | sequences at corresponding indexes. 10 | 11 | Keyword arguments: 12 | :param Observable cls: Class 13 | :param Tuple args: Observable sources. 14 | 15 | :return: Returns an observable sequence containing lists of 16 | elements at corresponding indexes. 17 | :rtype: Observable 18 | """ 19 | 20 | def result(*args): 21 | return list(args) 22 | 23 | args += (result,) 24 | return Observable.zip(*args) 25 | -------------------------------------------------------------------------------- /rx/subjects/__init__.py: -------------------------------------------------------------------------------- 1 | from .subject import Subject 2 | from .behaviorsubject import BehaviorSubject 3 | from .replaysubject import ReplaySubject 4 | from .asyncsubject import AsyncSubject -------------------------------------------------------------------------------- /rx/subjects/anonymoussubject.py: -------------------------------------------------------------------------------- 1 | from rx.core import ObservableBase 2 | 3 | 4 | class AnonymousSubject(ObservableBase): 5 | def __init__(self, observer, observable): 6 | super(AnonymousSubject, self).__init__() 7 | 8 | self.observer = observer 9 | self.observable = observable 10 | 11 | def _subscribe_core(self, observer): 12 | return self.observable.subscribe(observer) 13 | 14 | def on_completed(self): 15 | self.observer.on_completed() 16 | 17 | def on_error(self, exception): 18 | self.observer.on_error(exception) 19 | 20 | def on_next(self, value): 21 | self.observer.on_next(value) 22 | -------------------------------------------------------------------------------- /rx/subjects/innersubscription.py: -------------------------------------------------------------------------------- 1 | from rx import config 2 | 3 | 4 | class InnerSubscription(object): 5 | def __init__(self, subject, observer): 6 | self.subject = subject 7 | self.observer = observer 8 | 9 | self.lock = config["concurrency"].RLock() 10 | 11 | def dispose(self): 12 | with self.lock: 13 | if not self.subject.is_disposed and self.observer: 14 | if self.observer in self.subject.observers: 15 | self.subject.observers.remove(self.observer) 16 | self.observer = None 17 | -------------------------------------------------------------------------------- /rx/testing/__init__.py: -------------------------------------------------------------------------------- 1 | from . import reactivetest, reactive_assert 2 | from .testscheduler import TestScheduler 3 | from .reactivetest import OnNextPredicate, OnErrorPredicate, ReactiveTest, is_prime 4 | from .mockdisposable import MockDisposable -------------------------------------------------------------------------------- /rx/testing/coldobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import ObservableBase, Observer, AnonymousObserver, Disposable 2 | from rx.disposables import CompositeDisposable 3 | 4 | from .subscription import Subscription 5 | from .reactive_assert import AssertList 6 | 7 | 8 | class ColdObservable(ObservableBase): 9 | def __init__(self, scheduler, messages): 10 | super(ColdObservable, self).__init__() 11 | 12 | self.scheduler = scheduler 13 | self.messages = messages 14 | self.subscriptions = AssertList() 15 | 16 | def subscribe(self, on_next=None, on_error=None, on_completed=None, observer=None): 17 | # Be forgiving and accept an un-named observer as first parameter 18 | if isinstance(on_next, Observer): 19 | observer = on_next 20 | elif not observer: 21 | observer = AnonymousObserver(on_next, on_error, on_completed) 22 | 23 | return self._subscribe_core(observer) 24 | 25 | def _subscribe_core(self, observer): 26 | clock = self.scheduler.to_relative(self.scheduler.now) 27 | self.subscriptions.append(Subscription(clock)) 28 | index = len(self.subscriptions) - 1 29 | disposable = CompositeDisposable() 30 | 31 | def get_action(notification): 32 | def action(scheduler, state): 33 | notification.accept(observer) 34 | return Disposable.empty() 35 | return action 36 | 37 | for message in self.messages: 38 | notification = message.value 39 | 40 | # Don't make closures within a loop 41 | action = get_action(notification) 42 | disposable.add(self.scheduler.schedule_relative(message.time, action)) 43 | 44 | def dispose(): 45 | start = self.subscriptions[index].subscribe 46 | end = self.scheduler.to_relative(self.scheduler.now) 47 | self.subscriptions[index] = Subscription(start, end) 48 | disposable.dispose() 49 | 50 | return Disposable.create(dispose) 51 | -------------------------------------------------------------------------------- /rx/testing/dump.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from rx import Observable, AnonymousObservable 4 | from rx.internal import extensionmethod 5 | 6 | 7 | @extensionmethod(Observable) 8 | def dump(self, name = "test"): 9 | """Debug method for inspecting an observable sequence 10 | 11 | Keyword parameters: 12 | name -- [Optional] A name to make it easier to match the debug output if 13 | you insert multiple dumps into the same observable sequence. 14 | 15 | Return an unmodified observable sequence 16 | """ 17 | 18 | def subscribe(observer): 19 | def on_next(value): 20 | print("{%s}-->{%s}" % (name, value)) 21 | observer.on_next(value) 22 | def on_error(ex): 23 | print("{%s} error -->{%s}" % (name, ex)) 24 | observer.on_error(ex) 25 | def on_completed(): 26 | print("{%s} completed" % name) 27 | observer.on_completed() 28 | 29 | return self.subscribe(on_next, on_error, on_completed) 30 | return AnonymousObservable(subscribe) 31 | 32 | -------------------------------------------------------------------------------- /rx/testing/hotobservable.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observer, AnonymousObserver, ObservableBase, Disposable 2 | from .subscription import Subscription 3 | from .reactive_assert import AssertList 4 | 5 | 6 | class HotObservable(ObservableBase): 7 | def __init__(self, scheduler, messages): 8 | super(HotObservable, self).__init__() 9 | 10 | self.scheduler = scheduler 11 | self.messages = messages 12 | self.subscriptions = AssertList() 13 | self.observers = [] 14 | 15 | observable = self 16 | 17 | def get_action(notification): 18 | def action(scheduler, state): 19 | for observer in observable.observers[:]: 20 | notification.accept(observer) 21 | return Disposable.empty() 22 | return action 23 | 24 | for message in self.messages: 25 | notification = message.value 26 | 27 | # Warning: Don't make closures within a loop 28 | action = get_action(notification) 29 | scheduler.schedule_absolute(message.time, action) 30 | 31 | def subscribe(self, on_next=None, on_error=None, on_completed=None, observer=None): 32 | # Be forgiving and accept an un-named observer as first parameter 33 | if isinstance(on_next, Observer): 34 | observer = on_next 35 | elif not observer: 36 | observer = AnonymousObserver(on_next, on_error, on_completed) 37 | 38 | return self._subscribe_core(observer) 39 | 40 | def _subscribe_core(self, observer): 41 | observable = self 42 | self.observers.append(observer) 43 | self.subscriptions.append(Subscription(self.scheduler.clock)) 44 | index = len(self.subscriptions) - 1 45 | 46 | def dispose_action(): 47 | observable.observers.remove(observer) 48 | start = observable.subscriptions[index].subscribe 49 | end = observable.scheduler.clock 50 | observable.subscriptions[index] = Subscription(start, end) 51 | 52 | return Disposable.create(dispose_action) 53 | -------------------------------------------------------------------------------- /rx/testing/mockdisposable.py: -------------------------------------------------------------------------------- 1 | from .reactive_assert import AssertList 2 | 3 | 4 | class MockDisposable(): 5 | def __init__(self, scheduler): 6 | self.scheduler = scheduler 7 | self.disposes = AssertList() 8 | self.disposes.append(self.scheduler.clock) 9 | 10 | def dispose(self): 11 | self.disposes.append(self.scheduler.clock) 12 | -------------------------------------------------------------------------------- /rx/testing/mockobserver.py: -------------------------------------------------------------------------------- 1 | from rx.core import Observer 2 | from rx.core.notification import OnNext, OnError, OnCompleted 3 | 4 | from .recorded import Recorded 5 | from .reactive_assert import AssertList 6 | 7 | 8 | class MockObserver(Observer): 9 | 10 | def __init__(self, scheduler): 11 | self.scheduler = scheduler 12 | self.messages = AssertList() 13 | 14 | def on_next(self, value): 15 | self.messages.append(Recorded(self.scheduler.clock, OnNext(value))) 16 | 17 | def on_error(self, exception): 18 | self.messages.append(Recorded(self.scheduler.clock, OnError(exception))) 19 | 20 | def on_completed(self): 21 | self.messages.append(Recorded(self.scheduler.clock, OnCompleted())) 22 | -------------------------------------------------------------------------------- /rx/testing/reactive_assert.py: -------------------------------------------------------------------------------- 1 | from rx.internal.basic import default_comparer 2 | 3 | def create_message(actual, expected): 4 | return '\r\n\tExpected: [%s]\r\n\tActual: [%s]' % (str(expected), str(actual)) 5 | 6 | def are_elements_equal(expected, actual, comparer=None, message=None): 7 | is_ok = True 8 | comparer = comparer or default_comparer 9 | if len(expected) != len(actual): 10 | msg = 'Not equal length. Expected: %s Actual: %s' % (len(expected), len(actual)) 11 | assert False, msg 12 | return 13 | 14 | for i, ex in enumerate(expected): 15 | is_ok = comparer(ex, actual[i]) 16 | if not is_ok: 17 | break 18 | 19 | assert is_ok, message or create_message(actual, expected) 20 | 21 | def assert_equal(expected, *actual): 22 | actual = list(actual) 23 | return are_elements_equal(expected, actual, default_comparer) 24 | 25 | class AssertList(list): 26 | def assert_equal(self, *expected): 27 | expected = list(expected) 28 | return are_elements_equal(expected, self, default_comparer) 29 | -------------------------------------------------------------------------------- /rx/testing/reactivetest.py: -------------------------------------------------------------------------------- 1 | import math 2 | import types 3 | 4 | from rx.core.notification import OnNext, OnError, OnCompleted 5 | from .recorded import Recorded 6 | from .subscription import Subscription 7 | 8 | 9 | def is_prime(i): 10 | """Tests if number is prime or not""" 11 | 12 | if i <= 1: 13 | return False 14 | 15 | _max = int(math.floor(math.sqrt(i))) 16 | for j in range(2, _max+1): 17 | if not i % j: 18 | return False 19 | 20 | return True 21 | 22 | 23 | # New predicate tests 24 | class OnNextPredicate(object): 25 | def __init__(self, predicate): 26 | self.predicate = predicate 27 | 28 | def __eq__(self, other): 29 | if other == self: 30 | return True 31 | if other is None: 32 | return False 33 | if other.kind != 'N': 34 | return False 35 | return self.predicate(other.value) 36 | 37 | 38 | class OnErrorPredicate(object): 39 | def __init__(self, predicate): 40 | self.predicate = predicate 41 | 42 | def __eq__(self, other): 43 | if other == self: 44 | return True 45 | if other is None: 46 | return False 47 | if other.kind != 'E': 48 | return False 49 | return self.predicate(other.exception) 50 | 51 | 52 | class ReactiveTest(object): 53 | created = 100 54 | subscribed = 200 55 | disposed = 1000 56 | 57 | @classmethod 58 | def on_next(cls, ticks, value): 59 | if isinstance(value, types.FunctionType): 60 | return Recorded(ticks, OnNextPredicate(value)) 61 | 62 | return Recorded(ticks, OnNext(value)) 63 | 64 | @classmethod 65 | def on_error(cls, ticks, exception): 66 | if isinstance(exception, types.FunctionType): 67 | return Recorded(ticks, OnErrorPredicate(exception)) 68 | 69 | return Recorded(ticks, OnError(exception)) 70 | 71 | @classmethod 72 | def on_completed(cls, ticks): 73 | return Recorded(ticks, OnCompleted()) 74 | 75 | @classmethod 76 | def subscribe(cls, start, end): 77 | return Subscription(start, end) 78 | -------------------------------------------------------------------------------- /rx/testing/recorded.py: -------------------------------------------------------------------------------- 1 | from rx.internal.basic import default_comparer 2 | 3 | 4 | class Recorded(object): 5 | def __init__(self, time, value, comparer=None): 6 | self.time = time 7 | self.value = value 8 | self.comparer = comparer or default_comparer 9 | 10 | def __eq__(self, other): 11 | """Returns true if a recorded value matches another recorded value""" 12 | 13 | time_match = self.time == other.time 14 | return time_match and self.comparer(self.value, other.value) 15 | 16 | equals = __eq__ 17 | 18 | def __repr__(self): 19 | return str(self) 20 | 21 | def __str__(self): 22 | return "%s@%s" % (self.value, self.time) 23 | -------------------------------------------------------------------------------- /rx/testing/subscription.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class Subscription(object): 5 | def __init__(self, start, end=None): 6 | self.subscribe = start 7 | self.unsubscribe = end or sys.maxsize 8 | 9 | def equals(self, other): 10 | return self.subscribe == other.subscribe and self.unsubscribe == other.unsubscribe 11 | 12 | def __eq__(self, other): 13 | return self.equals(other) 14 | 15 | def __repr__(self): 16 | return str(self) 17 | 18 | def __str__(self): 19 | unsubscribe = "Infinite" if self.unsubscribe == sys.maxsize else self.unsubscribe 20 | return "(%s, %s)" % (self.subscribe, unsubscribe) 21 | -------------------------------------------------------------------------------- /sprytile_preview.py: -------------------------------------------------------------------------------- 1 | preview_verts = None 2 | preview_uvs = None 3 | preview_is_quads = False 4 | 5 | def set_preview_data(verts, uvs, is_quads=True): 6 | """ 7 | Set the preview data for SprytileGUI to draw 8 | :param verts: 9 | :param uvs: 10 | :param is_quads: 11 | :return: 12 | """ 13 | global preview_verts, preview_uvs, preview_is_quads 14 | 15 | preview_verts = verts 16 | preview_uvs = uvs 17 | preview_is_quads = is_quads 18 | 19 | 20 | def clear_preview_data(): 21 | global preview_verts, preview_uvs, preview_is_quads 22 | 23 | preview_verts = None 24 | preview_uvs = None 25 | preview_is_quads = True -------------------------------------------------------------------------------- /sprytile_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ologon/Sprytile/c63be50d14b07192ff134ceab256f0d69b9c4c92/sprytile_tools/__init__.py --------------------------------------------------------------------------------