modules: modpython: Implement Module.AddCommand()

Currently, there is no usable wrapper for CModCommand for use within
bindings, so this commit adds a proxy class that adds itself as a
callback and allows implementing Python classes to implement commands
via __call__().

A completely synthetic example:

import znc

class foo(znc.Module):
    module_types = [znc.CModInfo.UserModule]

    def OnLoad(self, args, message):
        self.AddHelpCommand()
        self.AddCommand(FooCmd)

        return True

class FooCmd(znc.Command):
    cmd = 'foo'
    args = foo.t_d('bar')
    desc = foo.t_d('baz')

    def __call__(self, line):
        self.GetModule().PutModule('I have been foo’d!')

Fixes https://github.com/znc/znc/issues/198
This commit is contained in:
Ernestas Kulik
2022-07-04 00:03:30 +03:00
parent fab1bb1bd5
commit 94f1c32729
4 changed files with 110 additions and 2 deletions

View File

@@ -334,3 +334,35 @@ class CModulesIter {
CModules* m_pModules;
CModules::const_iterator m_it;
};
class ZNC_EXPORT_LIB_EXPORT CPyModCommand : public CModCommand {
CPyModule* m_pModule;
CModPython* m_pModPython;
PyObject* m_pyObj;
void operator()(const CString& sLine);
public:
CPyModCommand(CPyModule* pModule,
const CString& sCmd, const COptionalTranslation& sArgs,
const COptionalTranslation& sDesc, PyObject *pyObj)
: CModCommand(sCmd, [=](const CString& sLine) { (*this)(sLine); }, sArgs,
sDesc),
m_pModule(pModule),
m_pModPython(pModule->GetModPython()),
m_pyObj(pyObj) {
Py_INCREF(pyObj);
pModule->AddCommand(*this);
}
virtual ~CPyModCommand();
CPyModule* GetModule();
};
inline CPyModCommand* CreatePyModCommand(CPyModule* pModule,
const CString& sCmd,
const COptionalTranslation& sArgs,
const COptionalTranslation& sDesc,
PyObject* pyObj) {
return new CPyModCommand(pModule, sCmd, sArgs, sDesc, pyObj);
}

View File

@@ -197,7 +197,9 @@ class Module:
num)
return fmt.format
# TODO is "t_d" needed for python? Maybe after AddCommand is implemented
@classmethod
def t_d(cls, english, context=''):
return CDelayedTranslation('znc-' + cls.__name__, context, english)
def OnLoad(self, sArgs, sMessage):
return True
@@ -295,7 +297,7 @@ class Module:
pass
def OnModCommand(self, sCommand):
pass
self.HandleCommand(sCommand)
def OnModNotice(self, sMessage):
pass
@@ -429,6 +431,16 @@ class Module:
def OnSendToIRC(self, sLine):
pass
# Command stuff
def AddCommand(self, cls, *args, **kwargs):
cmd = cls(*args, **kwargs)
cmd._cmodcommand = CreatePyModCommand(self._cmod, cls.cmd,
COptionalTranslation(cls.args),
COptionalTranslation(cls.desc),
cmd)
return cmd
# Global modules
def OnAddUser(self, User, sErrorRet):
pass
@@ -672,6 +684,18 @@ class Module:
pass
class Command:
cmd = ''
args = ''
desc = ''
def __call__(self, sLine):
pass
def GetModule(self):
return self._cmodcommand.GetModule().GetNewPyObj()
def make_inherit(cl, parent, attr):
def make_caller(parent, name, attr):
return lambda self, *a: parent.__dict__[name](self.__dict__[attr], *a)
@@ -688,6 +712,7 @@ def make_inherit(cl, parent, attr):
make_inherit(Socket, CPySocket, '_csock')
make_inherit(Module, CPyModule, '_cmod')
make_inherit(Timer, CPyTimer, '_ctimer')
make_inherit(Command, CPyModCommand, '_cmodcommand')
class ZNCModuleLoader(importlib.abc.SourceLoader):