On Monday, May 15, 2017 at 10:28:49 AM UTC-5, Edward K. Ream wrote:
In conversation with Rebecca last night at dinner I saw more clearly what
> the next step is.
>
> *tl;dr:* We need an interface *class *that delivers (wrappers to) Leo's
> position and/or vnode classes instead of some (as yet unknown) npyscreen
> *array*.
>
The way is now open to create this class.
This post describes a collapse in complexity in the LeoMLTree and
LeoTreeData classes. This post also gives a very fast caching scheme for
redrawing npyscreen outlines.
This post continues the original Engineering Notebook post. It will be of
interest only to a few. It will be pre-writing for a post to the npyscreen
list.
*Two non issues*
What I will say later might be taken as a criticism of the npyscreen code
base. But any such complaints would be silly. It would be extremely
dangerous and unwise to make *any* non-local changes to the npyscreen
sources. Doing so would almost surely break existing npyscreen apps. I
have felt free to make minor *local* simplifications to the sources in the
leo/external/npyscreen folder, but that's all.
The quality of the npyscreen docs is also a non-issue. Sources, not words,
are the ultimate underlying truth. After brief perusal of the npyscreen
docs, I have only read the npyscreen sources.
*Collapsing the LeoMLTree class*
I am going to speak plainly. Elsewhere I have described the npyscreen
sources as a maze. I have spent days wandering the maze trying to unravel
the code. Much of the maze is irritating, useless, C-like, blah, blah blah.
In particular, the base npyscreen code seems deathly afraid of crashing.
There is no reason for this fear in Python. During development, *crashes
help the app's developer find problems*. In the end, it's up to the *app's
developer* to eliminate crashes.
Happily, it's possible for subclasses of MLTree, such as LeoMLTree, to cut
through this maze without touching MLTree at all. Now, LeoMLTree is dead
simple. Here is how I did it:
1. *Eliminated weak references*. Weak references infect the npyscreen
sources like a rash. In fact, there is not the slightest reason to use them
in the tree code, and many good reasons not to do so. For starters, weak
references complicate debugging. Imo, removing weak references was an
essential first step toward navigating the maze.
2. *Simplified the all-important values property*. The actual drawing code
uses what looks like a values *array.* It took a *long* time to see that
the values *property *simulates an array whose values may be cached. Here
are the over-ridden getter and setter methods, as defined in LeoMLTree:
def _setValues(self, tree):
self._myFullValues = tree or LeoTreeData()
def _getValues(self):
'''
Return the (possibly cached) list returned by
self._myFullValues.get_tree_as_list().
Setting _cached_tree to None invalidates the cache.
'''
if getattr(self, '_cached_tree', None):
return self._cached_tree_as_list
else:
self._cached_tree = self._myFullValues
self._cached_tree_as_list =
self._myFullValues.get_tree_as_list()
return self._cached_tree_as_list
This code is *way* simpler than the corresponding code in the base MLTree
class. But even this simpler code is faux clever, for at least three,
reasons:
1. Code in MLTree (or LeoMLTree) must explicitly disable the cache by
setting the _cached_tree ivar to None. This makes the client code aware of
a nerdy little helper ivar.
2. Instead of setting _cached_tree to None, the client code *might as well
refresh the cache directly*, say by calling self.refresh_values(). So the
values property has no real purpose. To anticipate a bit, Leo will need to
refresh/invalidate the cache in exactly one place, namely in its
c.frame.tree.redraw method.
3. The values property doesn't even do a very good job of caching.
Refreshing the cache creates a TreeData node for *every visible node* in
the tree. This could be a problem for large trees. A better caching scheme,
given below, creates TreeData (LeoTreeData) nodes only when actually
referenced.
*Clever caching*
At last I can describe how the big switcheroo will work:
- A values *class *will replace the values *property*.
- The class will have a __getitem__ method that will return only the
requested LeoTreeData item. Something like this (untested):
def __getitem__(self, n):
return self.get_data(n)
def get_data(self, n)
'''Return a LeoTreeData for the n'th visible position of the
outline.'''
c = self.c
# Look for n or n-1 in the caches.
data = self.data_cache.get(n)
if data:
return data
p = self.position_cache.get(max(0,n-1))
if p:
p = p.moveToNextVisible()
if p:
self.position_cache[n] = p
self.data_cache[n] = data = LeoTreeData(p)
return data
else:
return None
# Search the tree, caching the result.
i, p = 0, c.rootPosition()
while p:
if i == n:
self.position_cache[n] = p
self.data_cache[n] = data = LeoTreeData(p)
return data
else:
p.moveToNextVisible()
return None
This is clever code. In practice, it will have close to O(0) running time!
Indeed, LeoMLTree._redraw (adapted from MultiLine.update) *access lines in
numeric order.* As a result, a search of the Leo outline will be needed for
only the first displayed line. Thereafter, n-1 will exist in the position
cache, so only a single call to p.moveToNextVisible() will needed.
*Summary*
Subclasses of MLTree (and TreeData) can be much simpler than the
corresponding base classes. The actual LeoMLTree and LeoTreeData classes
contain many more examples than shown here. I hope the actual code will
help developers of npyscreen devs navigate the maze.
Significantly better caching of TreeData objects is possible.
LeoMLTree.values[i] should be faster than MLTree.values[i], even
(especially) for very large Leo outlines. The caching scheme shown above
reduces the stress on the GC by greatly reducing the number of allocated
LeoTreeData objects.
c.frame.tree.redraw will clear the two caches in a new LeoValues class and
then call LeoMLTree.display().
Edward
--
You received this message because you are subscribed to the Google Groups
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.