tkinter façade: a callable option that receives Tk's OWN arguments
bind and -command hand a Python callable either nothing or one Event, and
the dispatcher (pxxcb) covers both. Tk also calls options with its own
argument lists, and those have no path through the façade yet:
| option | Tk calls it with |
|---|---|
-yscrollcommand / -xscrollcommand |
first last (two fractions) |
a scrollbar's -command |
moveto <frac> or scroll <n> units|pages |
-validatecommand, -postcommand, trace handlers |
their own argument sets |
For the scroll pair this does not matter today: CPython's tkinter does not call
back into Python for them either — it wires Tcl straight to the other widget's
subcommand, and TkiOptScrollCmd now does the same (receiver → widget path,
option name → subcommand). Anything that is NOT a widget method is refused
loudly there rather than wired to something wrong.
What is missing is the general shape:
- a registry entry that records "this callback takes Tcl's raw arguments";
- a dispatcher branch that packs
argv[2..]into Python values instead of building an Event; - call bridges for 2 and 3 arguments —
pycallback_call2/3for a bound method, and a bound-fn / closure that accepts more than one own parameter (todaypyboundfn_call_ptrpasses exactly one).
Until it lands, a plain def or lambda as a scroll command fails with a message naming the limit.
Why it was filed
songformatter's settings.py writes both canonical spellings
(Scrollbar(..., command=self.canvas.yview) and
canvas.configure(yscrollcommand=self.scrollbar.set)). Passing a bound method
into the façade's former AnsiString option compiled silently and handed Tcl a
garbage script; the event loop then hung inside update. See
[[bug-nilpy-bound-method-coerced-to-string]].
Moved to blocked/ 2026-07-31 — the missing piece is runtime, not façade
Re-read against the code rather than the ticket text. Of the three things listed under "what is missing", the first two are façade work Track B owns — a registry flag and a dispatcher branch. The third is not:
call bridges for 2 and 3 arguments —
pycallback_call2/3for a bound method, and a bound-fn / closure that accepts more than one own parameter (todaypyboundfn_call_ptrpasses exactly one).
pycallback_call0/1 live in compiler/builtin/pylib.pas:581 and
pyboundfn_call_ptr in pyeval.pas:1870 — shared NilPy runtime, which Track B
does not edit. Without them the façade half has nothing to call, so building
the registry and the dispatcher branch first would land dead code.
Split and filed as [[feature-nilpy-multi-arg-callback-bridges]] (Track N).
Tagged for later: when that lands, the remaining work here is small and
entirely in lib/pcl/tkinter.pas.
Nothing regresses in the meantime. The case that motivated this ticket — the
scrollbar pair — turned out not to need it at all: CPython's tkinter does not
call back into Python for yscrollcommand either, it wires Tcl straight to the
other widget's subcommand, and TkiOptScrollCmd now does the same and refuses
loudly for anything that is not a widget method. Nothing enters the regression
suite from here until the bridges exist.
2026-08-03 — dependency recorded in frontmatter
The 2026-07-31 note above identified the blocker in prose (the shared-runtime
pycallback_call2/3 / multi-arg pyboundfn_call_ptr bridges) but no
blocked-by: edge existed, so the board could not see it: this ticket ranked as
ready and would have been handed to a Track B agent who cannot edit
compiler/builtin/pylib.pas. The bridges have their own ticket —
[[feature-nilpy-multi-arg-callback-bridges]] — and it is now the recorded edge,
so priority propagates to it and this surfaces only once it lands.