MLD302: Unseeded global random state read inside a frame callback¶
qual reports MLD302 when a provably per-frame context (updater,
always_redraw, stop condition, interpolate override, ...) calls a
module-level function of stdlib random (random, randint, uniform,
choice, shuffle, ...) or of numpy.random, reading the process-global
random state.
- Default severity:
warning - Minimum confidence:
medium - Implementation phase:
4 - Fix: none
Every render then produces different frames: the output is not reproducible, cached partial movies never match, and visual diffing in CI is impossible.
Deliberately not flagged:
- locally seeded generators —
rng = random.Random(42)/gen = numpy.random.default_rng(7)and their instance methods; - files that seed the global state: a
random.seed(...)(ornumpy.random.seed(...)) call anywhere in the file conservatively downgrades the corresponding family to silence; - cold contexts — global randomness in
constructruns once and is layout, not per-frame nondeterminism; - any name rebound elsewhere in the file (resolution never guesses).
Wrong:
dot.add_updater(lambda m: m.set_x(random.uniform(-1, 1))) # new frames every run
dot.add_updater(lambda m: m.set_y(np.random.uniform(-1, 1)))
Right: