pythonpython-3.xwolframalphastopiteration

What is iteration error in Wolframalpha api and how can I fix it?


So I'm working on making a simple research bot but I've run into a problem. I was following a guide on using wolfram alpha in python and when I test it I sometimes get the error

Traceback (most recent call last):
  File "python", line 6, in <module>
StopIteration`. 

Here is my code:

import wolframalpha
import wikipedia
client = wolframalpha.Client('my_id')
q=input('Problem: ')
res = client.query(q)
print(next(res.results).text)

It only happens with some queries and it often works, but still its rather annoying. I looked online but didn't find any help, so I don't know if this is new or something is wrong with my code. Anyway, here is a link to a repl I made where its not working here. Try it with "uranium" I know that one brings the error and so do a few others I've tried. Thanks!


Solution

  • This error is telling you that your query had no results.


    This line:

    print(next(res.results).text)
    

    … calls next on an iterator, res.results, without a default value:

    Retrieve the next item from the iterator by calling its __next__() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised.

    If res had no results to show you, res.results is an empty iterator. Meaning it's exhausted right from the start, so when you call next on it, you're going to get StopIteration.

    And just passing a default isn't going to do much good here. Consider this:

        print(next(res.results, None).text)
    

    Now, if there are no results, next will return your default value None, and you'll immediately try to do None.text, which will just raise an AttributeError.


    One way to fix this is to just handle the error:

    try:
        print(next(res.results).text)
    except StopIteration:
        print('No results')
    

    Another is to break that compound expression up into simpler ones, so you can use a default:

    result = next(res.results, None)
    print(res.text if res else 'No results')
    

    However, res can include 2 or 3 results just as easily as 0—that's the whole reason it returns an iterator. And usually, you're going to want all of them, or at least a few of them. If that's the case, the best solution is to use a for loop. Iterators are born hoping they'll be used in a for loop, because it makes everyone's like easier:

    for result in res.results:
        print(result.text)
    

    This will do nothing if results is empty, print one result if there's only one, or print all of the results if there are multiple.

    If you're worried about getting 500 results when you only wanted a few, you can stop at, say, 3:

    for result in itertools.islice(res.results, 3):
        print(result.text)
    

    … or:

    for i, result in enumerate(res.results):
        print(result.text)
        if i > 2: break