0

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!

random_hooman
  • 1,660
  • 7
  • 23
Saradoc
  • 103
  • 1
  • 10
  • 1
    Whenever you ask a question that starts with "I was following a guide", please include a link to the guide if at all possible. Sometimes it's useless (but harmless) info, but sometimes it immediately tells us the answer, because someone here knows that it's an outdated guide and can link you to the new version, or because it's a notoriously bad guide that you just shouldn't following, or because the author of the guide is here and knows exactly what you're talking about, etc. – abarnert Jul 15 '18 at 19:45
  • Also, is that the complete traceback? Usually a traceback includes the line that actually caused the error. I'm _pretty sure_ it's the last one, but it would be better to know than to have to make an educated guess. – abarnert Jul 15 '18 at 19:46
  • 1
    It's because you have no result for this query. – Thierry Lathuille Jul 15 '18 at 19:48

1 Answers1

3

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
abarnert
  • 354,177
  • 51
  • 601
  • 671