← board

tkinter facade parameters named with a trailing underscore cannot be passed by name

txt.insert(index="end", chars="HELLO")
error: Nil Python: Text.insert has no parameter named 'index'

The facade declares them with a trailing underscore:

procedure insert(const index_: AnsiString; const chars: AnsiString);
procedure delete(const first_: AnsiString; const last_: AnsiString = '');
function  get(const first_: AnsiString; const last_: AnsiString = ''): AnsiString;

Positionally these work; by NAME they cannot be reached, and CPython's tkinter documents index= and first=/last= as the parameter names. The compiled lambda path binds keyword arguments by name against the real signature, so a mismatch is now a compile error rather than a silent mis-bind ([[bug-nilpy-pyeval-host-kwargs-positional]]) — which is how this surfaced.

Whatever the underscore was avoiding (index and get also name methods on the same or a sibling class), the exported NAME is part of the API and should match CPython's. If the Pascal identifier has to differ, the frontend needs a way to carry the Python-visible name — but check first whether the plain spelling compiles today.

Where to look

lib/pcl/tkinter.pas — census every _-suffixed parameter in one pass rather than fixing the three above, and check lower_ (a METHOD, same treatment) while there.

Gate

make lib-test plus a .npy calling the renamed parameters by keyword against CPython's spelling.

RESOLVED 2026-07-31 — the plain spelling compiles, so the API now carries it

Measured first, as the ticket asked. PXX parses a MEMBER name contextually, so a method or parameter may be called set, file, destroy or lower even though Pascal reserves or overloads those words — a two-program probe confirmed it before anything was renamed. Nothing needed a frontend change.

Renamed to CPython's own spelling, in one census pass:

was now where
index_, first_, last_ index, first, last Text.insert / delete / get / tag_add
path_ path Notebook.nametowidget
file_ file PhotoImage.Create
lower_ lower Widget
destroy_ destroy Widget
set_ set Scrollbar, StringVar, BooleanVar, and lib/rtl/configparser.pas
orient_ fOrient Scrollbar — an INTERNAL field, not part of the API; the Pascal f-prefix says so

The frontend's trailing-underscore fallbacks (parser.inc for the typed path, pyparser.inc for the variant path) are now a legacy compatibility route rather than the way these names are reached. They are left in place; nothing depends on them here any more.

What stays underscored, and why

END_ (and X_ / Y_ next to it) are module CONSTANTS, not members, and a const block is parsed as a declaration list — a const literally named END ends the block, and every constant after it silently disappears (that is how tk.CENTER once came back undefined). Verified again with a probe. The qualified Python spellings tk.END / tk.X / tk.Y already resolve through the frontend's mapping, so the application still writes CPython's name; only the Pascal-side identifier differs, at a declaration that documents why.

Fallout found and fixed

examples/tk/callbacks.npy DIED at runtime — 'Scrollbar' object has no attribute 'set'. The underscore fallbacks both guard on "a call follows", so a bound method taken as a VALUE (yscrollcommand=self.bar.set, the ordinary way a scrollbar is wired) never reached set_. With the method named set, the example runs: it is now asserted in lib-test, output and all, rather than merely compiled.

Gate

tools/gate.sh lib GREEN. New regression examples/tk/kwargs.npy — every renamed parameter passed BY KEYWORD, plus set/lower/destroy called by CPython's name — asserted in lib-test's tk-nilpy step under Xvfb, alongside callbacks.npy's full output.

Log