ENB: The grand strategy of annotating Leo

7 views
Skip to first unread message

Edward K. Ream

unread,
Apr 4, 2026, 3:31:44 PM (2 days ago) Apr 4
to leo-editor
This Engineering Notebook post discusses the second phase of PR #4576: making the annotations of widgets and wrappers as concrete as possible. It’s a big challenge.

Aha! Insert checkers based on apparent context

Leo should call the checkers (the  g.check* functions in leoGlobals.py) based on the apparent, static context of each checked argument. In most cases, the argument's name gives apparent context. As an exception, the StringTextWrapper annotation means (at present, it's merely a temporary stand-in) that the argument should have an annotation indicating one of the wrapper classes that comprise the high-level text interface. Let's look at some examples.

First, the checkers in Leo's core should be distinct from the checkers in Leo's qt gui files. Why? Because those checkers have separate static environments.

An argument's name usually suffices to indicate context, so, regardless of the initial annotation, an arg called frame or parentFrame should be checked with g.checkFrame (in Leo's core) or g.checkQtFrame (in any Qt-related plugin).

As I said, StringTextWrapper​ is a special case. It should be checked with g.checkText (in Leo's core) or g.checkQtText (in any Qt-related plugin). In other words, we provisionally trust that StringTextWrapper is a stand-in for an arg that conforms to Leo's high-level text interface. However, the reports from these two text checkers may show that we need​ to call another (possibly new) checker.

Do you see why this scheme is clever? We know, from the static context alone, what checker to call! We don't need to know beforehand what the correct annotation should be! This scheme cuts the Gordian knot of type inference by unifying static and dynamic inference.

This scheme is resilient and flexible. We can add new checkers if necessary. It's trivial to change our minds about what the ground truth of annotated objects should be.

The virtual user will be my guide

A previous ENB discussed running a virtual user in a unit test running in the Qt gui. The goals of this unit test will be:

- To call each instance of each g.checker method throughout Leo's code base.
  The test should fail otherwise.
- At first, to report the cumulative set of classes that each individual checker receives.
- Eventually, to verify that each individual checker receives objects of all and only classes of the expected kinds.

In this way, the unit test will guide both the annotations and the definitions of checkers.

Summary

The PR should insert calls to g.checkers based on the apparent context of arguments. The args name usually provides the context, but the StringTextWrapper annotation is an exception.

The virtual user, running in a Qt-based unit test, will suggest improvements to checkers and annotations.

For now, this scheme applies only to Leo. Eventually, I might try to generalize it to other Python programs, but that's a distant possibility.

I welcome all comments, questions, and suggestions.

Edward
Reply all
Reply to author
Forward
0 new messages