- Converter: def/return/global/nonlocal, class (inheritance, property, staticmethod, classmethod), lambda, *args/**kwargs and call-site unpacking. Unsupported dunder methods, generators and unknown decorators are still refused - Player: closures, super(), instances that survive save/load, isinstance, hasattr/getattr/setattr, map/filter/sorted(key=), recursion limit - audit_ir accepts calls to names, classes and methods the game defines itself; unknown renpy.* calls still block the import. Volume/pan/pause/replay calls are accepted as presentation-only no-ops; build.* settings are skipped - Tests: IR fixture compiled from tests/fixtures/classes.py (tests/make_ir.py) and run in the interpreter; converter tests updated Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
104 lines
2.2 KiB
Python
104 lines
2.2 KiB
Python
# Source of classes.ir.json. Regenerate from backend/ with:
|
|
# python -m tests.make_ir ../frontend/tests/fixtures/classes.py ../frontend/tests/fixtures/classes.ir.json
|
|
# The Python below is also valid CPython, so expected values can be checked there.
|
|
|
|
|
|
class Perk(object):
|
|
count = 0
|
|
|
|
def __init__(self, name, cost=1):
|
|
self.name = name
|
|
self.cost = cost
|
|
Perk.count += 1
|
|
|
|
def __str__(self):
|
|
return "Perk(%s)" % self.name
|
|
|
|
|
|
class Player(object):
|
|
def __init__(self, name, *perks, **extra):
|
|
self.name = name
|
|
self.perks = list(perks)
|
|
self.extra = extra
|
|
self._hp = 10
|
|
|
|
def has_perk(self, name):
|
|
return any([p.name == name for p in self.perks])
|
|
|
|
def add(self, perk):
|
|
self.perks.append(perk)
|
|
return self
|
|
|
|
@property
|
|
def hp(self):
|
|
return self._hp
|
|
|
|
@hp.setter
|
|
def hp(self, value):
|
|
self._hp = max(0, value)
|
|
|
|
@staticmethod
|
|
def make(name):
|
|
return Player(name)
|
|
|
|
@classmethod
|
|
def label(cls):
|
|
return cls is Player
|
|
|
|
|
|
class Hero(Player):
|
|
def __init__(self, name):
|
|
super().__init__(name, Perk("brave"), role="hero")
|
|
self.level = 1
|
|
|
|
def has_perk(self, name):
|
|
return name == "legend" or super().has_perk(name)
|
|
|
|
|
|
total = 0
|
|
|
|
|
|
def bump(step=1, *, scale=2):
|
|
global total
|
|
total += step * scale
|
|
return total
|
|
|
|
|
|
def fact(n):
|
|
return 1 if n <= 1 else n * fact(n - 1)
|
|
|
|
|
|
def counter():
|
|
n = 0
|
|
|
|
def inc():
|
|
nonlocal n
|
|
n += 1
|
|
return n
|
|
|
|
return inc
|
|
|
|
|
|
hero = Hero("Ann")
|
|
hero.add(Perk("fast", cost=3))
|
|
hero.hp = -5
|
|
call_args = [2]
|
|
call_kw = {"scale": 10}
|
|
result_brave = hero.has_perk("brave")
|
|
result_legend = hero.has_perk("legend")
|
|
result_missing = hero.has_perk("nope")
|
|
result_count = Perk.count
|
|
result_str = str(Perk("x"))
|
|
result_hp = hero.hp
|
|
result_role = hero.extra["role"]
|
|
result_static = Player.make("Bob").name
|
|
result_cls = Player.label()
|
|
result_bump = bump(*call_args, **call_kw)
|
|
result_total = total
|
|
result_fact = fact(6)
|
|
tick = counter()
|
|
tick()
|
|
result_closure = tick()
|
|
result_lambda = sorted([3, 1, 2], key=lambda v: -v)
|
|
result_isinstance = [isinstance(hero, Player), isinstance(hero, Perk), isinstance(1, int)]
|