Skip to content

MLD307: Wall-clock, filesystem, or network call inside a frame callback

qual reports MLD307 when a provably per-frame context (updater, always_redraw, stop condition, interpolate override, ...) calls:

  • the wall clock — time.time / perf_counter / monotonic (and _ns variants), datetime.now / utcnow / today;
  • the filesystem — builtin open, or Path(...).read_text()-style chains on a pathlib.Path construction;
  • the network — urllib.request.urlopen, requests.get-family verbs, socket.create_connection / gethostbyname / getaddrinfo.

  • Default severity: warning

  • Minimum confidence: medium
  • Implementation phase: 4
  • Fix: none

Render time is not wall time: a 60 FPS second of animation may take minutes to raster, so wall-clock reads make the geometry depend on how fast the machine renders — a different machine produces a different video. Filesystem and network calls additionally repeat on every frame, block the frame loop, and couple the output to external state.

Name resolution is conservative: callees resolve through the file's imports (the frontend's scope-aware resolution first), and a name rebound anywhere in the file — including a shadowed builtin open — is never trusted (silence). Cold contexts (construct runs once) stay silent; use the updater's dt parameter or a ValueTracker for time-based motion and load data once in construct.

Wrong:

label.add_updater(lambda m: m.set_value(time.time()))            # machine-dependent
label.add_updater(lambda m: m.set_value(len(open("f").read())))  # I/O per frame

Right:

data = float(Path("f").read_text())                       # once, in construct
label.add_updater(lambda m, dt: m.increment_value(dt))    # scene time, not wall time