 only
caught ValueError (xyz)

>>> import warnings
>>> old_filters = warnings.filters.copy()
>>> warnings.filterwarnings("ignore", category=DeprecationWarning)

# Filter DeprecationWarning: regarding the (type, val, tb) signature of throw().
# Deprecation warnings are re-enabled below.

>>> g.throw(ValueError, ValueError(1))   # value+matching type
caught ValueError (1)

>>> g.throw(ValueError, TypeError(1))  # mismatched type, rewrapped
caught ValueError (1)

>>> g.throw(ValueError, ValueError(1), None)   # explicit None traceback
caught ValueError (1)

>>> g.throw(ValueError(1), "foo")       # bad args
Traceback (most recent call last):
  ...
TypeError: instance exception may not have a separate value

>>> g.throw(ValueError, "foo", 23)      # bad args
Traceback (most recent call last):
  ...
TypeError: throw() third argument must be a traceback object

>>> g.throw("abc")
Traceback (most recent call last):
  ...
TypeError: exceptions must be classes or instances deriving from BaseException, not str

>>> g.throw(0)
Traceback (most recent call last):
  ...
TypeError: exceptions must be classes or instances deriving from BaseException, not int

>>> g.throw(list)
Traceback (most recent call last):
  ...
TypeError: exceptions must be classes or instances deriving from BaseException, not type

>>> def throw(g,exc):
...     try:
...         raise exc
...     except:
...         g.throw(*sys.exc_info())
>>> throw(g,ValueError) # do it with traceback included
caught ValueError ()

>>> g.send(1)
1

>>> throw(g,TypeError)  # terminate the generator
Traceback (most recent call last):
  ...
TypeError

>>> print(g.gi_frame)
None

>>> g.send(2)
Traceback (most recent call last):
  ...
StopIteration

>>> g.throw(ValueError,6)       # throw on closed generator
Traceback (most recent call last):
  ...
ValueError: 6

>>> f().throw(ValueError,7)     # throw on just-opened generator
Traceback (most recent call last):
  ...
ValueError: 7

>>> warnings.filters[:] = old_filters

# Re-enable DeprecationWarning: the (type, val, tb) exception representation is deprecated,
#                               and may be removed in a future version of Python.

Plain "raise" inside a generator should preserve the traceback (#13188).
The traceback should have 3 levels:
- g.throw()
- f()
- 1/0

>>> def f():
...     try:
...         yield
...     except:
...         raise
>>> g = f()
>>> try:
...     1/0
... except ZeroDivisionError as v:
...     try:
...         g.throw(v)
...     except Exception as w:
...         tb = w.__traceback__
>>> levels = 0
>>> while tb:
...     levels += 1
...     tb = tb.tb_next
>>> levels
3

Now let's try closing a generator:

>>> def f():
...     try: yield
...     except GeneratorExit:
...         print("exiting")

>>> g = f()
>>> next(g)
>>> g.close()
exiting
>>> g.close()  # should be no-op now

>>> f().close()  # close on just-opened generator should be fine

>>> def f(): yield      # an even simpler generator
>>> f().close()         # close before opening
>>> g = f()
>>> next(g)
>>> g.close()           # close normally

And finalization:

>>> def f():
...     try: yield
...     finally:
...         print("exiting")

>>> g = f()
>>> next(g)
>>> del g; gc_collect()  # For PyPy or other GCs.
exiting


GeneratorExit is not caught by except Exception:

>>> def f():
...     try: yield
...     except Exception:
...         print('except')
...     finally:
...         print('finally')

>>> g = f()
>>> next(g)
>>> del g; gc_collect()  # For PyPy or other GCs.
finally


Now let's try some ill-behaved generators:

>>> def f():
...     try: yield
...     except GeneratorExit:
...         yield "foo!"
>>> g = f()
>>> next(g)
>>> g.close()
Traceback (most recent call last):
  ...
RuntimeError: generator ignored GeneratorExit
>>> g.close()


Our ill-behaved code should be invoked during GC:

>>> with support.catch_unraisable_exception() as cm:
...     g = f()
...     next(g)
...     del g
...
...     cm.unraisable.exc_type == RuntimeError
...     "generator ignored GeneratorExit" in str(cm.unraisable.exc_value)
...     cm.unraisable.exc_traceback is not None
True
True
True

And errors thrown during closing should propagate:

>>> def f():
...     try: yield
...     except GeneratorExit:
...         raise TypeError("fie!")
>>> g = f()
>>> next(g)
>>> g.close()
Traceback (most recent call last):
  ...
TypeError: fie!


Ensure that various yield expression constructs make their
enclosing function a generator:

>>> def f(): x += yield
>>> type(f())
<class 'generator'>

>>> def f(): x = yield
>>> type(f())
<class 'generator'>

>>> def f(): lambda x=(yield): 1
>>> type(f())
<class 'generator'>

>>> def f(d): d[(yield "a")] = d[(yield "b")] = 27
>>> data = [1,2]
>>> g = f(data)
>>> type(g)
<class 'generator'>
>>> g.send(None)
'a'
>>> data
[1, 2]
>>> g.send(0)
'b'
>>> data
[27, 2]
>>> try: g.send(1)
... except StopIteration: pass
>>> data
[27, 27]

ak