...
All the recent changes arise from changing the annotation of p.v. The new (correct!) annotation is:self.v: VNode | None = v # PR #4767: Yes, p.v may be None.As you would expect, declaring that p.v may be None has ripple effects throughout Leo, especially in leoNodes.py, but to a lesser extent in leoCommands.py.DiscussionI would not be all that terrible to keep the old annotation for p.v. As mentioned above, it really doesn't matter whether mypy knows that p.v can be None.However, I see no reason not to correctly annotate p.v. New asserts tell mypy (and the human reader) that the code depends on p.v being a valid VNode instead of None. Moreover, the correct annotation allows the code to do without some type: ignore comments.
On Thu, Jul 2, 2026 at 1:01 PM Thomas Passin wrote:> I am really getting confused here. First you say that Yes, p.v may be None. Then you say "New asserts tell mypy (and the human reader) that the code depends on p.v being a valid VNode instead of None".> So the code cannot in fact accept p.v being None. These two statements seem to contradict each other.They do indeed. This is an excellent question.Happily, there's a simple explanation for the contradiction. Let's look at the big picture. Consider:for p in c.all_positions():whateverThe c.all_positions() generator will eventually set p.v = None, thereby making p "falsey".So the only correct annotation for p.v is Position | None.
>>> help(c.all_positions)
Help on method all_positions in module leo.core.leoCommands:
all_positions(copy: 'bool' = True) -> 'Generator' method of leo.core.leoCommands.Commands instance
A generator return all positions of the outline, in outline order.
On Thu, Jul 2, 2026 at 9:38 PM Thomas Passin <tbp1...@gmail.com> wrote:> I don't get this. The iterator should stop when there are no more positions available. I don't see why p.v should be None in this situation. p should be the last valid position the iterator returns.That's not what happens and it never will happen. The PR annotates what is, not what you or I want it to be.
In my workbook.leo outline,empties = []
for p in c.all_positions():
if not p.v:
empties.append(p.h)
g.es(len(empties)) # prints 0In what circumstances can the iterator end with a Position having p.v = None?
On Fri, Jul 3, 2026 at 7:05 AM Thomas Passin wrote:In my workbook.leo outline,empties = []
for p in c.all_positions():
if not p.v:
empties.append(p.h)
g.es(len(empties)) # prints 0In what circumstances can the iterator end with a Position having p.v = None?Every time, except when there is a break or return inside the loop.That doesn't matter in this example, because 'empties' isn't the generator.