MLP215: provably no-op updater still costs per-frame work¶
qual reports MLP215 when a registered updater's body provably
does nothing — it never mutates its parameter, never writes captured
state, and calls nothing unresolved (mutates_target == No,
calls_unknown == No, and the pure-effect verdict holds) — yet still
costs per-frame work through one of two separate evidence channels
(DESIGN 7.3):
- (a) dynamicized waits — a
dt-parameter mobject updater or a Scene updater makes waits dynamic (DESIGN 3.3). The rule attributes a wait only when the updater is provably the sole dynamicizing source: no stop condition,always_update_mobjectsoff, no other scene updater, no other possibly-time-based family updater, and no unknown or intrinsically per-frame object in the family. -
(b) moving scope — any family updater makes its host a moving member of a play's Cairo scope (
scene.py get_moving_mobjects; suspension does not matter there). Quantified with the display-order position only when the host is the exact first moving member of aKnownorder; qualitative otherwise. -
Default severity:
warning - Minimum confidence:
high - Implementation phase:
3 - Fix: none (removal is the user's call; the body may be a stub)
Binding catalog near miss: a one-argument no-op updater does not dynamicize a plain wait — with no play in sight the rule stays silent.
Wrong¶
class Demo(Scene):
def construct(self):
square = Square()
self.add(square)
square.add_updater(lambda m, dt: None) # no-op, but dt-based
self.wait(3) # renders every frame