Python sandbox - try/except/else/finally, raise (incl. re-raise), assert, for/while ... else - Multi-generator and dict comprehensions - Builtin exception hierarchy; user exceptions subclass it - Inside try, missing keys/indexes, division by zero, attribute access on None and unknown names raise Python exceptions; lookups stay lenient elsewhere - int()/float() raise ValueError on non-numeric strings ATL - converter/atl.py: set/move (linear, ease, easein, easeout), pause, repeat, parallel, choice, block, on show/hide and named transforms become a timeline shipped as game.json "atl"; Move() becomes a linear slide - player/atl.ts: timeline runner wired into the renderer; `on hide` plays before the picture is removed - function, time, contains, matrixcolor and expressions still block the import Tests: exceptions/ATL fixtures and runtime tests, converter ATL tests; README and ARCHITECTURE updated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
100 lines
1.7 KiB
Python
100 lines
1.7 KiB
Python
# Source of exceptions.ir.json. Regenerate from backend/ with:
|
|
# python -m tests.make_ir ../frontend/tests/fixtures/exceptions.py ../frontend/tests/fixtures/exceptions.ir.json
|
|
|
|
|
|
class AppError(Exception):
|
|
def __init__(self, msg, code=0):
|
|
super().__init__(msg)
|
|
self.code = code
|
|
|
|
|
|
def risky(n):
|
|
if n == 0:
|
|
raise AppError("zero", code=7)
|
|
return 10 // n
|
|
|
|
|
|
log = []
|
|
try:
|
|
risky(0)
|
|
except AppError as e:
|
|
log.append(str(e))
|
|
result_code = e.code
|
|
else:
|
|
log.append("no")
|
|
finally:
|
|
log.append("fin")
|
|
|
|
try:
|
|
d = {"a": 1}
|
|
v = d["b"]
|
|
except KeyError:
|
|
result_key = "keyerror"
|
|
|
|
try:
|
|
xs = [1]
|
|
xs[5]
|
|
except (IndexError, ValueError):
|
|
result_index = "indexerror"
|
|
|
|
try:
|
|
int("abc")
|
|
except ValueError:
|
|
result_value = "valueerror"
|
|
|
|
try:
|
|
risky(0)
|
|
except Exception:
|
|
result_generic = True
|
|
|
|
try:
|
|
result_zero = 1 / 0
|
|
except ZeroDivisionError:
|
|
result_zero = "zerodiv"
|
|
|
|
|
|
def retry():
|
|
for i in range(3):
|
|
try:
|
|
if i < 2:
|
|
raise ValueError("again")
|
|
return i
|
|
except ValueError:
|
|
continue
|
|
return -1
|
|
|
|
|
|
result_retry = retry()
|
|
|
|
found = None
|
|
for n in [1, 3, 5]:
|
|
if n % 2 == 0:
|
|
found = n
|
|
break
|
|
else:
|
|
found = "none"
|
|
result_forelse = found
|
|
|
|
k = 0
|
|
while k < 3:
|
|
k += 1
|
|
else:
|
|
k = k * 10
|
|
result_whileelse = k
|
|
|
|
try:
|
|
try:
|
|
raise RuntimeError("inner")
|
|
except RuntimeError:
|
|
raise
|
|
except RuntimeError as e3:
|
|
result_reraise = str(e3)
|
|
|
|
result_pairs = [(x, y) for x in [1, 2] for y in [3, 4]]
|
|
result_dictcomp = {a: b for a in [1, 2] for b in [a * 10] if b > 10}
|
|
|
|
try:
|
|
assert 1 == 2, "boom"
|
|
except AssertionError as e4:
|
|
result_assert = str(e4)
|