On Saturday, November 25, 2017 at 3:41:56 AM UTC-6, Edward K. Ream wrote:
Later posts will discuss the problems that arose, the choices I made and
> what I considered and rejected.
>
This post provides all remaining details.
*tl:dr:* Read the summary. But the details are the reason for this post ;-)
> To the first approximation, the cmds branch does the following, and
*nothing* else:
*1. Defines most...commander commands outside of leoCommands.py.*
The @g.commander_command decorator injects its decorated routine into c.
Modulo the usual decorator blah, blah, blah, this decorator does the
following:
funcToMethod(func, leoCommands.Commands)
for c in app.commanders():
c.k.registerCommand(self.name, func)
This is *exactly* the same as the g.command decorator, except for the call
to g.funcToMethod.
g.funcToMethod is straight out of the Python Cookbook. It adds the
*function* func as a *method* of the Commands class. Here is its docstring:
QQQ
The following method allows you to add a function as a method of any class.
That is, it converts the function to a method of the class. The method just
added is available instantly to all existing instances of the class, and to
all instances created in the future.
The function's first argument should be self.
The newly created method has the same name as the function unless the
optional name argument is supplied, in which case that name is used as the
method name.
QQQ
The decorator passes only two arguments to g.funcToMethod, so the injected
name is *always *the decorated function's name.
The only even slightly confusing part of all this is that the function's
first argument is "self". This is a common Python pattern (it's in the
Cookbook!), so it shouldn't stress us devs unduly ;-)
*Aside about aliases*: To create an alias to a command method *in c* we can
use g.command_alias:
def command_alias(alias, func):
'''
Create an alias for the *already defined* method
in the Commands class.
'''
import leo.core.leoCommands as leoCommands
assert hasattr(leoCommands.Commands, func.__name__)
funcToMethod(func, leoCommands.Commands, alias)
Two such calls exist at present, ensuring an obsessive/pedantic kind of
compatibility with existing Leo scripts and unit tests.
*2. Removes some of c's old helper methods from c's namespace.*
Previously these all these helpers were methods of c. Mostly that just
polluted c's namespace and confused some devs who wanted to know *exactly*
what the official c methods were.
Some simple, repetitive munging was required whenever a command used
helpers. To see the munging in action let's look at the "c_ec.extract &
helpers" node from commanderEditCommands.py. There are four helpers,
namely the node's four children.
A. For each helper x I did the following:
- Replaced "c_ec.x" by "def x" in the headline, indicating that the helper
is now a function.
- Replaced "self" by "c" in the helper's signature.
- Removed the assignment, "c = self".
B. In c_ec.extract I did the following:
- Called x(c,...) instead of c.x(...)
And that's it.
*3. Generalizes how c.doCommand and c.k.callAltXFunction dispatch commands.*
The new code is *c.executeAnyCommand*, called only from the above two
methods. It uses Python's inspect module to determine how to call each
command.
I am in two minds about this approach. One the plus side, it potentially
could allow us to avoid some kinds of wrappers. On the minus side, it's
almost too ugly to use inspect.
I'm not sure why this is needed at all. For now it is. I'm investigating...
*What I didn't do*
Yesterday I considered and (thankfully) rejected the notion of replacing
g.commander_command with "redirector" methods in c. Like this, for c.save:
def save(*args, **kwargs):
import leo.commands.commanderFileCommands as cfc
__doc__ = cfc.save.__doc__
cfc.save(*args, **keys)
Yes, this is an explicit statement that c.save is implemented elsewhere,
but it is unbearably ugly and stupid.
My original justification for this was that "explicit is better than
implicit". I came to my senses when I realized that @g.commander_command is
explicit enough ;-)
*Summary*
The cmds branch moves most commands formerly defined in leoCommands.py into
other files. This has obvious packaging advantages.
As a happy side effects, *rarely-used* helper methods no longer pollute c's
namespace. It will be easy to reinstate any helper that an existing script
actually uses. I suspect that won't be necessary.
Aside from hiding helper names, the changes made should be *completely
invisible* to the rest of Leo, and to Leo scripts.
The remaining members of c consist of *useful* utilities. They aren't ever
going to disappear. They are now organized by their function so that devs
can see much more clearly what's available. This answers a FAQ about what
the "official" methods of c are.
The main work of this branch was repetitive code changes. Developing this
branch is the closest I have ever come to Test Driven Development.
Pyflakes, pylint and unit tests quickly found almost all blunders.
All question welcome.
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.