mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Move signals into moulinette core and uptade interfaces
* Replace ActionsMapSignals to MoulinetteSignals and make it available through 'msignal' global variable * Update interfaces to support signals changes * Add a new signal 'display' and implement it in the cli
This commit is contained in:
parent
13213025a1
commit
183ed78dc4
6 changed files with 143 additions and 123 deletions
|
@ -50,8 +50,9 @@ def init(**kwargs):
|
|||
"""
|
||||
import sys
|
||||
import __builtin__
|
||||
from moulinette.core import Package, install_i18n
|
||||
from moulinette.core import Package, MoulinetteSignals, install_i18n
|
||||
__builtin__.__dict__['pkg'] = Package(**kwargs)
|
||||
__builtin__.__dict__['msignals'] = MoulinetteSignals()
|
||||
|
||||
# Initialize internationalization
|
||||
install_i18n()
|
||||
|
|
|
@ -11,89 +11,6 @@ from collections import OrderedDict
|
|||
from moulinette.core import (MoulinetteError, MoulinetteLock)
|
||||
from moulinette.interfaces import BaseActionsMapParser
|
||||
|
||||
## Actions map Signals -------------------------------------------------
|
||||
|
||||
class ActionsMapSignals(object):
|
||||
"""Actions map's Signals interface
|
||||
|
||||
Allow to easily connect signals of the actions map to handlers. They
|
||||
can be given as arguments in the form of { signal: handler }.
|
||||
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
# Initialize handlers
|
||||
for s in self.signals:
|
||||
self.clear_handler(s)
|
||||
|
||||
# Iterate over signals to connect
|
||||
for s, h in kwargs.items():
|
||||
self.set_handler(s, h)
|
||||
|
||||
def set_handler(self, signal, handler):
|
||||
"""Set the handler for a signal"""
|
||||
if signal not in self.signals:
|
||||
raise ValueError("unknown signal '%s'" % signal)
|
||||
setattr(self, '_%s' % signal, handler)
|
||||
|
||||
def clear_handler(self, signal):
|
||||
"""Clear the handler of a signal"""
|
||||
if signal not in self.signals:
|
||||
raise ValueError("unknown signal '%s'" % signal)
|
||||
setattr(self, '_%s' % signal, self._notimplemented)
|
||||
|
||||
|
||||
## Signals definitions
|
||||
|
||||
"""The list of available signals"""
|
||||
signals = { 'authenticate', 'prompt' }
|
||||
|
||||
def authenticate(self, authenticator, help):
|
||||
"""Process the authentication
|
||||
|
||||
Attempt to authenticate to the given authenticator and return
|
||||
it.
|
||||
It is called when authentication is needed (e.g. to process an
|
||||
action).
|
||||
|
||||
Keyword arguments:
|
||||
- authenticator -- The authenticator object to use
|
||||
- help -- A help message for the authenticator
|
||||
|
||||
Returns:
|
||||
The authenticator object
|
||||
|
||||
"""
|
||||
if authenticator.is_authenticated:
|
||||
return authenticator
|
||||
return self._authenticate(authenticator, help)
|
||||
|
||||
def prompt(self, message, is_password=False, confirm=False):
|
||||
"""Prompt for a value
|
||||
|
||||
Prompt the interface for a parameter value which is a password
|
||||
if 'is_password' and must be confirmed if 'confirm'.
|
||||
Is is called when a parameter value is needed and when the
|
||||
current interface should allow user interaction (e.g. to parse
|
||||
extra parameter 'ask' in the cli).
|
||||
|
||||
Keyword arguments:
|
||||
- message -- The message to display
|
||||
- is_password -- True if the parameter is a password
|
||||
- confirm -- True if the value must be confirmed
|
||||
|
||||
Returns:
|
||||
The collected value
|
||||
|
||||
"""
|
||||
return self._prompt(message, is_password, confirm)
|
||||
|
||||
@staticmethod
|
||||
def _notimplemented(**kwargs):
|
||||
raise NotImplementedError("this signal is not handled")
|
||||
|
||||
shandler = ActionsMapSignals()
|
||||
|
||||
|
||||
## Extra parameters ----------------------------------------------------
|
||||
|
||||
# Extra parameters definition
|
||||
|
@ -176,7 +93,7 @@ class AskParameter(_ExtraParameter):
|
|||
|
||||
try:
|
||||
# Ask for the argument value
|
||||
return shandler.prompt(message)
|
||||
return msignals.prompt(message)
|
||||
except NotImplementedError:
|
||||
return arg_value
|
||||
|
||||
|
@ -208,7 +125,7 @@ class PasswordParameter(AskParameter):
|
|||
|
||||
try:
|
||||
# Ask for the password
|
||||
return shandler.prompt(message, True, True)
|
||||
return msignals.prompt(message, True, True)
|
||||
except NotImplementedError:
|
||||
return arg_value
|
||||
|
||||
|
@ -398,20 +315,6 @@ class ActionsMap(object):
|
|||
else:
|
||||
return auth()
|
||||
|
||||
def connect(self, signal, handler):
|
||||
"""Connect a signal to a handler
|
||||
|
||||
Connect a signal emitted by actions map while processing to a
|
||||
handler. Note that some signals need a return value.
|
||||
|
||||
Keyword arguments:
|
||||
- signal -- The name of the signal
|
||||
- handler -- The method to handle the signal
|
||||
|
||||
"""
|
||||
global shandler
|
||||
shandler.set_handler(signal, handler)
|
||||
|
||||
def process(self, args, timeout=0, **kwargs):
|
||||
"""
|
||||
Parse arguments and process the proper action
|
||||
|
@ -533,7 +436,7 @@ class ActionsMap(object):
|
|||
parser.set_defaults(_extra=extras)
|
||||
|
||||
# Instantiate parser
|
||||
top_parser = self._parser_class(shandler)
|
||||
top_parser = self._parser_class()
|
||||
|
||||
# Iterate over actions map namespaces
|
||||
for n, actionsmap in actionsmaps.items():
|
||||
|
|
|
@ -130,6 +130,109 @@ class Package(object):
|
|||
True if mode[0] == 'w' else False)
|
||||
return open('%s/%s' % (self.get_cachedir(**kwargs), filename), mode)
|
||||
|
||||
class MoulinetteSignals(object):
|
||||
"""Signals connector for the moulinette
|
||||
|
||||
Allow to easily connect signals from the moulinette to handlers. A
|
||||
signal is emitted by calling the relevant method which call the
|
||||
handler.
|
||||
For the moment, a return value can be requested by a signal to its
|
||||
connected handler - make them not real-signals.
|
||||
|
||||
Keyword arguments:
|
||||
- kwargs -- A dict of {signal: handler} to connect
|
||||
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
# Initialize handlers
|
||||
for s in self.signals:
|
||||
self.clear_handler(s)
|
||||
|
||||
# Iterate over signals to connect
|
||||
for s, h in kwargs.items():
|
||||
self.set_handler(s, h)
|
||||
|
||||
def set_handler(self, signal, handler):
|
||||
"""Set the handler for a signal"""
|
||||
if signal not in self.signals:
|
||||
raise ValueError("unknown signal '%s'" % signal)
|
||||
setattr(self, '_%s' % signal, handler)
|
||||
|
||||
def clear_handler(self, signal):
|
||||
"""Clear the handler of a signal"""
|
||||
if signal not in self.signals:
|
||||
raise ValueError("unknown signal '%s'" % signal)
|
||||
setattr(self, '_%s' % signal, self._notimplemented)
|
||||
|
||||
|
||||
## Signals definitions
|
||||
|
||||
"""The list of available signals"""
|
||||
signals = { 'authenticate', 'prompt', 'display' }
|
||||
|
||||
def authenticate(self, authenticator, help):
|
||||
"""Process the authentication
|
||||
|
||||
Attempt to authenticate to the given authenticator and return
|
||||
it.
|
||||
It is called when authentication is needed (e.g. to process an
|
||||
action).
|
||||
|
||||
Keyword arguments:
|
||||
- authenticator -- The authenticator object to use
|
||||
- help -- A help message for the authenticator
|
||||
|
||||
Returns:
|
||||
The authenticator object
|
||||
|
||||
"""
|
||||
if authenticator.is_authenticated:
|
||||
return authenticator
|
||||
return self._authenticate(authenticator, help)
|
||||
|
||||
def prompt(self, message, is_password=False, confirm=False):
|
||||
"""Prompt for a value
|
||||
|
||||
Prompt the interface for a parameter value which is a password
|
||||
if 'is_password' and must be confirmed if 'confirm'.
|
||||
Is is called when a parameter value is needed and when the
|
||||
current interface should allow user interaction (e.g. to parse
|
||||
extra parameter 'ask' in the cli).
|
||||
|
||||
Keyword arguments:
|
||||
- message -- The message to display
|
||||
- is_password -- True if the parameter is a password
|
||||
- confirm -- True if the value must be confirmed
|
||||
|
||||
Returns:
|
||||
The collected value
|
||||
|
||||
"""
|
||||
return self._prompt(message, is_password, confirm)
|
||||
|
||||
def display(self, message, style='info'):
|
||||
"""Display a message
|
||||
|
||||
Display a message with a given style to the user.
|
||||
It is called when a message should be printed to the user if the
|
||||
current interface allows user interaction (e.g. print a success
|
||||
message to the user).
|
||||
|
||||
Keyword arguments:
|
||||
- message -- The message to display
|
||||
- style -- The type of the message. Possible values are:
|
||||
info, success, warning
|
||||
|
||||
"""
|
||||
try:
|
||||
self._display(message, style)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _notimplemented(**kwargs):
|
||||
raise NotImplementedError("this signal is not handled")
|
||||
|
||||
|
||||
# Interfaces & Authenticators management -------------------------------
|
||||
|
||||
|
|
|
@ -17,16 +17,13 @@ class BaseActionsMapParser(object):
|
|||
the global and actions configuration.
|
||||
|
||||
Keyword arguments:
|
||||
- shandler -- A actionsmap.ActionsMapSignals instance
|
||||
- parent -- A parent BaseActionsMapParser derived object
|
||||
|
||||
"""
|
||||
def __init__(self, shandler, parent=None):
|
||||
def __init__(self, parent=None):
|
||||
if parent:
|
||||
self.shandler = parent.shandler
|
||||
self._o = parent
|
||||
else:
|
||||
self.shandler = shandler
|
||||
self._o = self
|
||||
self._global_conf = {}
|
||||
self._conf = {}
|
||||
|
|
|
@ -100,7 +100,7 @@ class _ActionsMapPlugin(object):
|
|||
|
||||
def __init__(self, actionsmap):
|
||||
# Connect signals to handlers
|
||||
actionsmap.connect('authenticate', self._do_authenticate)
|
||||
msignals.set_handler('authenticate', self._do_authenticate)
|
||||
|
||||
self.actionsmap = actionsmap
|
||||
# TODO: Save and load secrets?
|
||||
|
@ -268,7 +268,7 @@ class _ActionsMapPlugin(object):
|
|||
def _do_authenticate(self, authenticator, help):
|
||||
"""Process the authentication
|
||||
|
||||
Handle the actionsmap._AMapSignals.authenticate signal.
|
||||
Handle the core.MoulinetteSignals.authenticate signal.
|
||||
|
||||
"""
|
||||
s_id = request.get_cookie('session.id')
|
||||
|
@ -314,8 +314,8 @@ class ActionsMapParser(BaseActionsMapParser):
|
|||
the arguments is represented by a argparse.ArgumentParser object.
|
||||
|
||||
"""
|
||||
def __init__(self, shandler, parent=None):
|
||||
super(ActionsMapParser, self).__init__(shandler, parent)
|
||||
def __init__(self, parent=None):
|
||||
super(ActionsMapParser, self).__init__(parent)
|
||||
|
||||
self._parsers = {} # dict({(method, path): _HTTPArgumentParser})
|
||||
|
||||
|
@ -398,7 +398,7 @@ class ActionsMapParser(BaseActionsMapParser):
|
|||
auth_conf, klass = self.get_conf(tid, 'authenticator')
|
||||
|
||||
# TODO: Catch errors
|
||||
auth = self.shandler.authenticate(klass(), **auth_conf)
|
||||
auth = msignals.authenticate(klass(), **auth_conf)
|
||||
if not auth.is_authenticated:
|
||||
# TODO: Set proper error code
|
||||
raise MoulinetteError(errno.EACCES, _("This action need authentication"))
|
||||
|
|
|
@ -13,8 +13,10 @@ colors_codes = {
|
|||
'red' : 31,
|
||||
'green' : 32,
|
||||
'yellow': 33,
|
||||
'cyan' : 34,
|
||||
'purple': 35
|
||||
'blue' : 34,
|
||||
'purple': 35,
|
||||
'cyan' : 36,
|
||||
'white' : 37
|
||||
}
|
||||
|
||||
def colorize(astr, color):
|
||||
|
@ -73,8 +75,8 @@ class ActionsMapParser(BaseActionsMapParser):
|
|||
- parser -- The argparse.ArgumentParser object to use
|
||||
|
||||
"""
|
||||
def __init__(self, shandler, parent=None, parser=None):
|
||||
super(ActionsMapParser, self).__init__(shandler, parent)
|
||||
def __init__(self, parent=None, parser=None):
|
||||
super(ActionsMapParser, self).__init__(parent)
|
||||
|
||||
self._parser = parser or argparse.ArgumentParser()
|
||||
self._subparsers = self._parser.add_subparsers()
|
||||
|
@ -107,7 +109,7 @@ class ActionsMapParser(BaseActionsMapParser):
|
|||
|
||||
"""
|
||||
parser = self._subparsers.add_parser(name, help=category_help)
|
||||
return self.__class__(None, self, parser)
|
||||
return self.__class__(self, parser)
|
||||
|
||||
def add_action_parser(self, name, tid, action_help=None, **kwargs):
|
||||
"""Add a parser for an action
|
||||
|
@ -129,7 +131,7 @@ class ActionsMapParser(BaseActionsMapParser):
|
|||
auth_conf, klass = self.get_conf(ret._tid, 'authenticator')
|
||||
|
||||
# TODO: Catch errors
|
||||
auth = self.shandler.authenticate(klass(), **auth_conf)
|
||||
auth = msignals.authenticate(klass(), **auth_conf)
|
||||
if not auth.is_authenticated:
|
||||
# TODO: Set proper error code
|
||||
raise MoulinetteError(errno.EACCES, _("This action need authentication"))
|
||||
|
@ -152,8 +154,9 @@ class Interface(BaseInterface):
|
|||
"""
|
||||
def __init__(self, actionsmap):
|
||||
# Connect signals to handlers
|
||||
actionsmap.connect('authenticate', self._do_authenticate)
|
||||
actionsmap.connect('prompt', self._do_prompt)
|
||||
msignals.set_handler('authenticate', self._do_authenticate)
|
||||
msignals.set_handler('display', self._do_display)
|
||||
msignals.set_handler('prompt', self._do_prompt)
|
||||
|
||||
self.actionsmap = actionsmap
|
||||
|
||||
|
@ -183,7 +186,7 @@ class Interface(BaseInterface):
|
|||
def _do_authenticate(self, authenticator, help):
|
||||
"""Process the authentication
|
||||
|
||||
Handle the actionsmap._AMapSignals.authenticate signal.
|
||||
Handle the core.MoulinetteSignals.authenticate signal.
|
||||
|
||||
"""
|
||||
# TODO: Allow token authentication?
|
||||
|
@ -193,17 +196,30 @@ class Interface(BaseInterface):
|
|||
def _do_prompt(self, message, is_password, confirm):
|
||||
"""Prompt for a value
|
||||
|
||||
Handle the actionsmap._AMapSignals.prompt signal.
|
||||
Handle the core.MoulinetteSignals.prompt signal.
|
||||
|
||||
"""
|
||||
if is_password:
|
||||
prompt = lambda m: getpass.getpass(colorize(_('%s: ') % m, 'cyan'))
|
||||
prompt = lambda m: getpass.getpass(colorize(_('%s: ') % m, 'blue'))
|
||||
else:
|
||||
prompt = lambda m: raw_input(colorize(_('%s: ') % m, 'cyan'))
|
||||
prompt = lambda m: raw_input(colorize(_('%s: ') % m, 'blue'))
|
||||
value = prompt(message)
|
||||
|
||||
if confirm:
|
||||
if prompt(_('Retype %s: ') % message) != value:
|
||||
if prompt(_('Retype %s') % message) != value:
|
||||
raise MoulinetteError(errno.EINVAL, _("Values don't match"))
|
||||
|
||||
return value
|
||||
|
||||
def _do_display(self, message, style):
|
||||
"""Display a message
|
||||
|
||||
Handle the core.MoulinetteSignals.display signal.
|
||||
|
||||
"""
|
||||
if style == 'success':
|
||||
print('%s %s' % (colorize(_("Success!"), 'green'), message))
|
||||
elif style == 'warning':
|
||||
print('%s %s' % (colorize(_("Warning!"), 'yellow'), message))
|
||||
else:
|
||||
print(message)
|
||||
|
|
Loading…
Reference in a new issue