global_commands_dict = {}
def command_method(name):
"""A decorator that creates a Leo command from a method."""
def _wrapper(f):
"""This wrapper is always called with one argument: the wrapped function."""
def inner_wrapper(self, event):
"""Inner wrapper docstring."""
return f(self, event)
inner_wrapper.__name__ = f.__name__ # Doesn't work.
inner_wrapper.__doc__ = f.__doc__ # works.
return inner_wrapper
global_commands_dict[name] = _wrapper
return _wrapper
class Test:
@command_method('get')
def get(self, event):
"""docstring for Test.get."""
g.trace(Test.get.__doc__)
event = g.Bunch(c=c)
Test().get(event)
g.printObj(global_commands_dict, tag='dict')get docstring for Test.get.
dict:
{
get:<function command_method.<locals>._wrapper at 0x00000170F5D90288>
}@dec2
@dec1
def func(arg1, arg2, ...):
pass
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
def command_method(name):
def _wrapper(f):
"""f is the wrapped function."""
def inner_wrapper(self, event=None, *args, **kwargs):
"""Inner wrapper docstring."""
return f(self, event, *args, **kwargs)
# inner_wrapper.__name__ = f.__name__ # happens too late to be useful.
f.is_command = True
f.command_name = name
inner_wrapper.__doc__ = f.__doc__ # works.
return inner_wrapper
global_commands_dict[name] = _wrapper
return _wrapper@g.command('undo')
def undo_command(event):
c = event.get('c')
if not c:
return
c.undoer.undo(event)def cmd(name):
"""Command decorator for the c.frame.body class."""
# pylint: disable=no-self-argument
return g.new_cmd_decorator(name, ['c', 'frame', 'body'])
import functools
global_commands_dict = {}
def command_method(name):
"""A decorator that creates a Leo command from a method."""
def _wrapper(f):
"""This wrapper is always called with one argument: the wrapped function."""
@functools.wraps(f)
def inner_wrapper(self, event):
"""Inner wrapper docstring."""
return f(self, event)
global_commands_dict[name] = inner_wrapper
return inner_wrapper
return _wrapper
class Test:
@command_method('get')
def get(self, event):
"""docstring for Test.get."""
g.trace(Test.get.__doc__)
event = g.Bunch(c=c)
Test().get(event)
g.printObj(global_commands_dict, tag='dict')
get docstring for Test.get.
dict:
{
get:<function Test.get at 0x7f1b980b8b90>
}I have changed your first test script to work. I am not sure if that is all you wanted.
@g.command_method('undo')
def undo(self, event=None):
... rest of code unchangedTypeError: inner_wrapper() missing 1 required positional argument: 'event'def inner_wrapper(self, event):def inner_wrapper(self, event=None):@test paste and undo in headline - at end
...
AssertionError: oops3 got: Test headline abcABCThe few times I've written decorators, I've always written the arguments as *args. That way avoids problems where inner methods/functions are called with/without self or default args. E.g.:
def command_method(name):
"""A decorator that creates a Leo command from a method."""
def _wrapper(f):
"""This wrapper is always called with one argument: the wrapped function."""
clsname = f.__qualname__.partition('.')[0]
@functools.wraps(f)
def inner_wrapper(self, event=None):
"""Inner wrapper docstring."""
getinst = globals().get(f'{clsname}_in_c')
c = self.c
instance = getinst(c)
return f(instance, event)
global_commands_dict[name] = inner_wrapper
return inner_wrapper
return _wrapper
g.Undoer_in_c = lambda c:c.undoerI think I have found the solution. I don't think it is possible to solve the problem just using decorators. The command_method decorator needs some help.
Sorry I did the work in commands not in commands2.Vitalije
git status
On branch commands2
Your branch is up to date with 'origin/commands2'.
Untracked files:
(use "git add ..." to include in what will be committed)
../config/myLeoSettings.leo
../core/qtree-profile.leo
../themes/dark/Icons/file_icons/
../../outlinediffs.py
../../toutlines.py
nothing added to commit but untracked files present (use "git add" to track)Well no it is not true, I did checkout commands2 and committed my work in commands2, and I did push.
The problem is likely on my end. I see your work on the commands2 branch on the github branches page.
ERROR: runTest (leo.core.leoTest.GeneralTestCase)
@test clone-marked-nodes
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\leo.repo\leo-editor\leo\core\leoTest.py", line 308, in runTest
exec(compile(script, scriptFile, 'exec'), d)
File "C:/leo.repo/leo-editor/leo/test/scriptFile.py", line 20, in <module>
test(p)
File "C:/leo.repo/leo-editor/leo/test/scriptFile.py", line 15, in test
c.undoer.undo()
File "c:\leo.repo\leo-editor\leo\core\leoGlobals.py", line 203, in inner_wrapper
instance = getinst(c)
TypeError: 'NoneType' object is not callableg.Undoer_in_c = lambda c:c.undoer
If you have installed by hand perhaps you've missed to change leoUndo.py ? At the end of the file you should add the line:g.Undoer_in_c = lambda c:c.undoer
If you have installed by hand perhaps you've missed to change leoUndo.py ? At the end of the file you should add the line:g.Undoer_in_c = lambda c:c.undoer
I didn't like it either. But I don't think that it is possible to solve this purely using decorator functions.