classification
Title: inconsistent stack trace for exceptions thrown in generators passed to min/max
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.6, Python 2.7
process
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: Naris R, serhiy.storchaka
Priority: normal Keywords:

Created on 2018-04-21 10:53 by Naris R, last changed 2018-04-21 15:19 by serhiy.storchaka. This issue is now closed.

Messages (4)
msg315561 - (view) Author: Naris R (Naris R) * Date: 2018-04-21 10:53
if a generator passed to min/max throws an exception, the stack trace is normally shown on the line that caused the exception, but if the exception is a StopIteration, the trace only shows the line where the max/min function was called.

I was writing a minimax and alphabeta search with generator expression and list comprehension and accidentally passed an empty iterator to the next function while computing states for minimax and it took a very long time to find out where the error was actually caused.
msg315568 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-04-21 13:06
Could you please provide a sample code?
msg315569 - (view) Author: Naris R (Naris R) * Date: 2018-04-21 13:19
This is a little bit contrived but it demonstrates the problem.

```
def good_exception():
    raise Exception('something bad happened')

def bad_exception():
    return next(iter([]))

def good(n):
    return good_exception() + n

def bad(n):
    return n - bad_exception()

import traceback

try:
    max(good(i) for i in range(4)) # desirable behaviour
except:
    traceback.print_exc()

try:
    min(bad(i) for i in range(7)) # unhelpful error message
except:
    traceback.print_exc()
```
msg315571 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-04-21 15:19
In Python 2.7 and 3.6 I got the following traceback for the second case:

Traceback (most recent call last):
  File "issue33323.py", line 21, in <module>
    min(bad(i) for i in range(7)) # unhelpful error message
ValueError: max() arg is an empty sequence

StopIteration in generator expressions didn't treated as an error. It just stopped the iteration. The exception is raised by the consumer of generated values, min(), because it doesn't work with empty sequences by default.

This behavior caused hard to investigate bugs and was considered harmful. Fortunately it will be fixed in Python 3.7. The traceback in 3.7:

Traceback (most recent call last):
  File "issue33323.py", line 21, in <genexpr>
    min(bad(i) for i in range(7)) # unhelpful error message
  File "issue33323.py", line 11, in bad
    return n - bad_exception()
  File "issue33323.py", line 5, in bad_exception
    return next(iter([]))
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "issue33323.py", line 21, in <module>
    min(bad(i) for i in range(7)) # unhelpful error message
RuntimeError: generator raised StopIteration

You can enable this behavior in earlier versions (3.5+) by adding the future import:

from __future__ import generator_stop

See PEP 479 for details.
History
Date User Action Args
2018-04-21 15:19:08serhiy.storchakasetstatus: open -> closed
messages: + msg315571

components: + Interpreter Core
resolution: out of date
stage: resolved
2018-04-21 13:19:47Naris Rsetmessages: + msg315569
2018-04-21 13:06:31serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg315568
2018-04-21 10:53:20Naris Rcreate