FA-90196 / Bytecode virtual machines / Open access
Generators: catch-all handler ignores thrown exceptions · case 01
A generator guarded by an Exception handler dies when throw() delivers ValueError.
ROOT CAUSE
Handler matching compares names exactly, so the catch-all never matches.
VERIFIED REPAIR
Match the handler when it names the thrown exception or is the catch-all.
Unsuccessful approach: Prefix matching of names does not model the catch-all either.
Case contract
A generator frame runs body ops push v, pop, add, yield (pop and hand out a value, suspend), return (pop, finish with StopIteration value), try exc target / endtry (handler stack; handler "Exception" catches any thrown name). Commands: next (= send None) and send v resume; resuming a suspended frame pushes the sent value as the result of its yield; sending non-None to a never-started generator is ["TypeError"]; a finished generator answers ["StopIteration", None]. throw into a created or finished generator finishes it with ["raised", exc]; otherwise the innermost matching handler is jumped to, or the generator finishes and re-raises. close finishes the generator. Frame faults are ["vm-error"].
Why this case matters
Generator state transitions (created/suspended/done) and send/throw semantics are easy to get subtly wrong.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and handlers[-1][0] != exc:
handlers.pop()
if not handlers:
state = 'done'
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['raised', 'ValueError'], ['StopIteration', None]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Failed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Passed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / 2aa55257b2a4901cbdd4b76d8545fc864cc891a66f76fc64d6bfcb27ec7578b2
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and not exc.startswith(handlers[-1][0]):
handlers.pop()
if not handlers:
state = 'done'
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['raised', 'ValueError'], ['StopIteration', None]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Failed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Passed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / e090c62c52af53c6cb00dc2911f0d53741f5a4e3d69805b93df9d7253bb83293
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and handlers[-1][0] not in (exc, 'Exception'):
handlers.pop()
if not handlers:
state = 'done'
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Passed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Passed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / f8a019999cd82806d62c44a3a1ff8aaf08de22e15580af70a42780f489ed07cb
Verification & scope
A deterministic, bounded teaching model of one bytecode virtual machine mechanism with a stipulated instruction encoding; it is not a production VM and claims no conformance to any real specification. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:24.495198+00:00.
Case digest / 19cce343908d94666112ad0caf9a4f5cba7541504576b07fb11c92b67cf1b6e0