A puzzle about the StopIteration exception

I don’t understand. There’s no silent conversion here - 1, 2 is a tuple. The [] operator takes a single operand, which is not the same thing as functions which take arguments. This example seems pretty much unrelated to the discussion at hand

I’ve twice linked you to the documentation for the args attribute, which applies to all exceptions. Since StopIteration is an exception, it has this attribute.

Read the documentation rather than relying exclusively on dir().

There are two ways for subclass to implement its function:

    1. entrust parent class to do it.
    1. use its special logic.

You can see both of them in many classes in many languages.

Here’s my solution, so far. I provide further evidence, which also puzzles me. Beyond that, I no longer claim to have removed my puzzlement. But perhaps I have reduced it. By the way, my underlying purpose it to well understand coroutines.

My evidence has the form of a test script, available on gitihub along with previous scripts. It’s purpose is to explore coroutine actions when an awaitable raises StopIteration(*args).

Here’s an extract, which provides a summary of results.

Coroutine actions when awaitable raises StopIteration(*args)

The test(*args) function returns a StopIteration exception, created
somewhat indirectly. We go via an async coroutine.

First an awaitable raises StopIteration(*args), in the context of a
coroutine that awaits an awaitable, and then returns its value.

We then catch and return the resulting StopIteration exception raised via the
coroutine. This is the value of test(*args).

The compare function effectively prints first the exception raised,
and then the exception received.

>>> def compare(*args):
...     print(repr(StopIteration(*args)))
...     print(repr(test(*args)))
...

>>> compare()
StopIteration()
StopIteration()

>>> compare(1)
StopIteration(1)
StopIteration(1)

>>> compare(1, 2)
StopIteration(1, 2)
StopIteration(1)

>>> compare(1, 2, 3)
StopIteration(1, 2, 3)
StopIteration(1)

>>> compare(None)
StopIteration(None)
StopIteration()

Your test script is comparing an unraised StopIteration to a raised one though. Wouldn’t a more correct comparison check the StopIteration raised by the coroutine to one that’s raised in a sync function?

Raising the exception will consume the args it was initialized with and set value to args[0].

Nice try. Let’s explore this a bit.

Here’s example where raising StopIteration seems to make no relevant difference. And raising an exception object produces the same object (although with of course a traceback added).

>>> exc_1 = StopIteration(1, 2)
>>> try:
...     raise exc_1
... except StopIteration as tmp:
...     exc_2 = tmp
...     
>>> exc_2
StopIteration(1, 2)
>>> exc_1 is exc_2
True
>>> exc_2
StopIteration(1, 2)

This is not happening in the example I just gave you. I suspect that in my earlier example we’re getting a different exception object (which most likely has a different id). With a bit of work my test script could answer that question also. But either way I would remain puzzled about what is happening and perhaps more importantly why.

That’s as documented - value defaults to None.

For clarity, here is the behaviour that is documented:

>>> StopIteration().value is None
True

So far as I know, the behaviour in my test script above is not documented. You’ll have to look at the gist on Github to see the precise definition of the function test that I used in the test script. That test function, as previously stated, records coroutine actions when an awaitable raisesStopIteration(*args).

Hm, I read it differently. The documentation imho quite clearly states that value is None by default, and that it is set to the argument given on constructing the exception object, i.e.exc = StopIteration(*args) => exc.value = exc.args[0] if args else None. The fact that exc.argscontains all arguments, including the first value is documented for BaseException from which StopIterationderives and thus inherits this behavior.

exception StopIteration

value

The exception object has a single attribute value, which is given as an argument when constructing the exception, and defaults to None.

exception BaseException

args

The tuple of arguments given to the exception constructor. …