0

I am attempting to integrate some ReactiveX concepts into an existing project, thinking it might be good practice and a way to make certain tasks cleaner.

I open a file, create an Observable from its lines, then do some filtering until I get just the lines I want. Now, I want to extract some information from two of those lines using re.search() to return particular groups. I can't for the life of me figure out how to get such values out of an Observable (without assigning them to globals).

train = 'ChooChoo'

with open(some_file) as fd:
    line_stream = Observable.from_(fd.readlines())

a_stream = line_stream.skip_while(
        # Begin at dictionary
        lambda x: 'config = {' not in x
    ).skip_while(
        # Begin at train key
        lambda x: "'" + train.lower() + "'" not in x
    ).take_while(
        # End at closing brace of dict value
        lambda x: '}' not in x
    ).filter(
        # Filter sdk and clang lines only
        lambda x: "'sdk'" in x or "'clang'" in x
    ).subscribe(lambda x: match_some_regex(x))

In place of .subscribe() at the end of that stream, I have tried using .to_list() to get a list over which I can iterate "the normal way," but it only returns a value of type:

<class 'rx.anonymousobservable.AnonymousObservable'>

What am I doing wrong here?

Every Rx example I have ever seen does nothing but print results. What if I want them in a data structure I can use synchronously?

Eric
  • 2,300
  • 3
  • 22
  • 29
  • You can only get the results *inside a callback in the `subscribe`*, that's the whole point of asynchronous observables - the collected results won't be available synchronously, you don't get them *"out"* so much *"in"*! Per [the docstring](https://github.com/ReactiveX/RxPY/blob/09ed65003d2e579753b7d0c257c5f5ac318076d9/rx/linq/observable/tolist.py#L9) (emphasis mine), `to_list` *"Returns **an observable sequence** containing a single element with a list containing all the elements of the source sequence."*. – jonrsharpe May 05 '16 at 22:22
  • Ok, then... how would I assign the two lines in that list to variables A and B in the local namespace? – Eric May 05 '16 at 22:28
  • *Which* local namespace? If you mean the one in which you also assign `a_stream`, you don't! At the point at which control returns to that scope, there is no guarantee that the async process is completed, *that's why you're observing it*. – jonrsharpe May 05 '16 at 22:32
  • Ahh, I see. Once I have observed the values I need (the two lines containing "sdk" and "clang") do I need to unsubscribe or terminate the Observable somehow to get those lines in a list? Imagine a similar scenario where you are asynchronously watching keystrokes and want to return the letters of the first 2 keystrokes. It has to be possible to operate synchronously from there on, right? – Eric May 05 '16 at 22:38
  • 1
    Yes, but *within the callback*, not in the scope from which you launched the process. It's not clear what benefit you think you're gaining from the observable in this case, just use `itertools`. – jonrsharpe May 05 '16 at 22:39
  • Great idea! Thanks, Jon! – Eric May 05 '16 at 22:56

1 Answers1

0

For the short term, I implemented the feature I wanted using itertools (as suggested by @jonrsharpe). Still the problem grated at back of my mind, so I came back to it today and figured it out.

This is not a good example of Rx, since it only uses a single thread, but at least now I know how to break out of "the monad" when need be.

#!/usr/bin/env python

from __future__ import print_function
from rx import *

def my_on_next(item):
    print(item, end="", flush=True)

def my_on_error(throwable):
    print(throwable)

def my_on_completed():
    print('Done')
    pass

def main():
    foo = []

    # Create an observable from a list of numbers
    a = Observable.from_([14, 9, 5, 2, 10, 13, 4])

    # Keep only the even numbers
    b = a.filter(lambda x: x % 2 == 0)

    # For every item, call a function that appends the item to a local list
    c = b.map(lambda x: foo.append(x))
    c.subscribe(lambda x: x, my_on_error, my_on_completed)

    # Use the list outside the monad!
    print(foo)

if __name__ == "__main__":
    main()

This example is rather contrived, and all the intermediate observables aren't necessary, but it demonstrates that you can easily do what I originally described.

Eric
  • 2,300
  • 3
  • 22
  • 29