remove Message objects + rewrite server startup

Change-Id: Ide72fb915c3ca93c74edadd8952853508e677de7
Reviewed-on: https://forge.frm2.tum.de/review/19199
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2018-10-15 14:24:34 +02:00
parent 9824b9216d
commit 87261382cf
30 changed files with 337 additions and 578 deletions

View File

@ -41,11 +41,15 @@ from __future__ import print_function
from time import time as currenttime
import threading
from secop.protocol.messages import Message, EVENTREPLY, IDENTREQUEST
from secop.protocol.errors import SECOPError, NoSuchModuleError, \
NoSuchCommandError, NoSuchParameterError, BadValueError, ReadonlyError
from secop.lib import formatExtendedStack, formatException
from secop.params import Parameter, Command
from secop.protocol.messages import EVENTREPLY, IDENTREQUEST, IDENTREPLY, \
ENABLEEVENTSREPLY, DESCRIPTIONREPLY, WRITEREPLY, COMMANDREPLY, \
DISABLEEVENTSREPLY, HEARTBEATREPLY
from secop.protocol.errors import InternalError, NoSuchModuleError, \
NoSuchCommandError, NoSuchParameterError, BadValueError, ReadonlyError, \
ProtocolError
from secop.params import Parameter
try:
unicode('a')
@ -56,9 +60,9 @@ except NameError:
class Dispatcher(object):
def __init__(self, logger, options):
def __init__(self, name, logger, options, srv):
# to avoid errors, we want to eat all options here
self.equipment_id = options[u'equipment_id']
self.equipment_id = name
self.nodeopts = {}
for k in list(options):
self.nodeopts[k] = options.pop(k)
@ -84,13 +88,12 @@ class Dispatcher(object):
if reallyall:
listeners = self._connections
else:
if getattr(msg, u'command', None) is None:
eventname = u'%s:%s' % (msg.module, msg.parameter
if msg.parameter else u'value')
else:
eventname = u'%s:%s()' % (msg.module, msg.command)
listeners = self._subscriptions.get(eventname, set()).copy()
listeners.update(self._subscriptions.get(msg.module, set()))
# all subscribers to module:param
listeners = self._subscriptions.get(msg[1], set()).copy()
# all subscribers to module
module = msg[1].split(':', 1)[0]
listeners.update(self._subscriptions.get(module, set()))
# all generic subscribers
listeners.update(self._active_connections)
for conn in listeners:
conn.queue_async_reply(msg)
@ -98,22 +101,26 @@ class Dispatcher(object):
def announce_update(self, moduleobj, pname, pobj):
"""called by modules param setters to notify subscribers of new values
"""
msg = Message(EVENTREPLY, module=moduleobj.name, parameter=pname)
msg.set_result(pobj.export_value(), dict(t=pobj.timestamp))
msg = (EVENTREPLY, u'%s:%s' % (moduleobj.name, pname), [pobj.export_value(), dict(t=pobj.timestamp)])
self.broadcast_event(msg)
def subscribe(self, conn, modulename, pname=u'value'):
def subscribe(self, conn, modulename, pname=None):
eventname = modulename
if pname:
eventname = u'%s:%s' % (modulename, pname)
self._subscriptions.setdefault(eventname, set()).add(conn)
def unsubscribe(self, conn, modulename, pname=u'value'):
eventname = modulename
def unsubscribe(self, conn, modulename, pname=None):
if pname:
eventname = u'%s:%s' % (modulename, pname)
else:
eventname = modulename
# also remove 'more specific' subscriptions
for k, v in self._subscriptions.items():
if k.startswith(u'%s:' % modulename):
v.discard(conn)
if eventname in self._subscriptions:
self._subscriptions.setdefault(eventname, set()).discard(conn)
self._subscriptions[eventname].discard(conn)
def add_connection(self, conn):
"""registers new connection"""
@ -125,6 +132,7 @@ class Dispatcher(object):
self._connections.remove(conn)
for _evt, conns in list(self._subscriptions.items()):
conns.discard(conn)
self._active_connections.discard(conn)
def register_module(self, moduleobj, modulename, export=True):
self.log.debug(u'registering module %r as %s (export=%r)' %
@ -138,15 +146,17 @@ class Dispatcher(object):
return self._modules[modulename]
elif modulename in list(self._modules.values()):
return modulename
raise NoSuchModuleError(module=unicode(modulename))
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
def remove_module(self, modulename_or_obj):
moduleobj = self.get_module(modulename_or_obj) or modulename_or_obj
moduleobj = self.get_module(modulename_or_obj)
modulename = moduleobj.name
if modulename in self._export:
self._export.remove(modulename)
self._modules.pop(modulename)
# XXX: also clean _subscriptions
self._subscriptions.pop(modulename, None)
for k in [k for k in self._subscriptions if k.startswith(u'%s:' % modulename)]:
self._subscriptions.pop(k, None)
def list_module_names(self):
# return a copy of our list
@ -158,7 +168,7 @@ class Dispatcher(object):
# omit export=False params!
res = []
for aname, aobj in self.get_module(modulename).accessibles.items():
if isinstance(aobj, Command) or aobj.export:
if aobj.export:
res.extend([aname, aobj.for_export()])
self.log.debug(u'list accessibles for module %s -> %r' %
(modulename, res))
@ -190,33 +200,32 @@ class Dispatcher(object):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError(module=modulename)
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
cmdspec = moduleobj.accessibles.get(command, None)
if cmdspec is None:
raise NoSuchCommandError(module=modulename, command=command)
if len(cmdspec.datatype.argtypes) != len(arguments):
raise BadValueError(
module=modulename,
command=command,
reason=u'Wrong number of arguments!')
raise NoSuchCommandError('Module has no such command!')
num_args_required = len(cmdspec.datatype.argtypes)
if num_args_required != len(arguments):
raise BadValueError(u'Wrong number of arguments (need %d, got %d)!' % (num_args_required, len(arguments)))
# now call func and wrap result as value
# note: exceptions are handled in handle_request, not here!
func = getattr(moduleobj, u'do_' + command)
res = func(*arguments)
# XXX: pipe through cmdspec.datatype.result ?
return res, dict(t=currenttime())
def _setParameterValue(self, modulename, pname, value):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError(module=modulename)
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
pobj = moduleobj.accessibles.get(pname, None)
if pobj is None or not isinstance(pobj, Parameter):
raise NoSuchParameterError(module=modulename, parameter=pname)
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
if pobj.readonly:
raise ReadonlyError(module=modulename, parameter=pname)
raise ReadonlyError('This parameter can not be changed remotely.')
writefunc = getattr(moduleobj, u'write_%s' % pname, None)
# note: exceptions are handled in handle_request, not here!
@ -231,11 +240,11 @@ class Dispatcher(object):
def _getParameterValue(self, modulename, pname):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError(module=modulename)
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
pobj = moduleobj.accessibles.get(pname, None)
if pobj is None or not isinstance(pobj, Parameter):
raise NoSuchParameterError(module=modulename, parameter=pname)
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
readfunc = getattr(moduleobj, u'read_%s' % pname, None)
if readfunc:
@ -253,155 +262,107 @@ class Dispatcher(object):
def handle_request(self, conn, msg):
"""handles incoming request
will call 'queue.request(data)' on conn to send reply before returning
will call 'queue_async_request(data)' on conn or return reply
"""
self.log.debug(u'Dispatcher: handling msg: %r' % msg)
# if there was an error in the frontend, bounce the resulting
# error msgObj directly back to the client
if msg.errorclass:
return msg
self.log.debug(u'Dispatcher: handling msg: %s' % repr(msg))
# play thread safe !
# XXX: ONLY ONE REQUEST (per dispatcher) AT A TIME
with self._lock:
if msg.action == IDENTREQUEST:
self.log.debug(u'Looking for handle_ident')
handler = self.handle_ident
else:
self.log.debug(u'Looking for handle_%s' % msg.action)
handler = getattr(self, u'handle_%s' % msg.action, None)
action, specifier, data = msg
# special case for *IDN?
if action == IDENTREQUEST:
action, specifier, data = 'ident', None, None
self.log.debug(u'Looking for handle_%s' % action)
handler = getattr(self, u'handle_%s' % action, None)
if handler:
try:
reply = handler(conn, msg)
if reply:
conn.queue_reply(reply)
return None
except SECOPError as err:
self.log.exception(err)
msg.set_error(err.name, unicode(err), {})#u'traceback': formatException(),
#u'extended_stack':formatExtendedStack()})
return msg
except (ValueError, TypeError) as err:
self.log.exception(err)
msg.set_error(u'BadValue', unicode(err), {u'traceback': formatException()})
print(u'--------------------')
print(formatExtendedStack())
print(u'====================')
return msg
except Exception as err:
self.log.exception(err)
msg.set_error(u'InternalError', unicode(err), {u'traceback': formatException()})
print(u'--------------------')
print(formatExtendedStack())
print(u'====================')
return msg
return handler(conn, specifier, data)
else:
self.log.error(u'Can not handle msg %r' % msg)
msg.set_error(u'Protocol', u'unhandled msg', {})
return msg
raise InternalError('unhandled message!')
# now the (defined) handlers for the different requests
def handle_help(self, conn, msg):
msg.mkreply()
return msg
def handle_help(self, conn, specifier, data):
self.log.error('should have been handled in the interface!')
def handle_ident(self, conn, msg):
msg.mkreply()
return msg
def handle_ident(self, conn, specifier, data):
return (IDENTREPLY, None, None)
def handle_describe(self, conn, msg):
# XXX:collect descriptive data
msg.setvalue(u'specifier', u'.')
msg.setvalue(u'data', self.get_descriptive_data())
msg.mkreply()
return msg
def handle_describe(self, conn, specifier, data):
return (DESCRIPTIONREPLY, '.', self.get_descriptive_data())
def handle_read(self, conn, msg):
# XXX: trigger polling and force sending event
if not msg.parameter:
msg.parameter = u'value'
msg.set_result(*self._getParameterValue(msg.module, msg.parameter))
def handle_read(self, conn, specifier, data):
if data:
raise ProtocolError('poll request don\'t take data!')
modulename, pname = specifier, u'value'
if ':' in specifier:
modulename, pname = specifier.split(':', 1)
# XXX: trigger polling and force sending event ???
return (EVENTREPLY, specifier, list(self._getParameterValue(modulename, pname)))
#if conn in self._active_connections:
# return None # already send to myself
#if conn in self._subscriptions.get(msg.module, set()):
# return None # already send to myself
msg.mkreply()
return msg # send reply to inactive conns
def handle_change(self, conn, specifier, data):
modulename, pname = specifier, u'value'
if ':' in specifier:
modulename, pname = specifier.split(u':', 1)
return (WRITEREPLY, specifier, list(self._setParameterValue(modulename, pname, data)))
def handle_change(self, conn, msg):
# try to actually write XXX: should this be done asyncron? we could
# just return the reply in that case
if not msg.parameter:
msg.parameter = u'target'
msg.set_result(*self._setParameterValue(msg.module, msg.parameter, msg.data))
#if conn in self._active_connections:
# return None # already send to myself
#if conn in self._subscriptions.get(msg.module, set()):
# return None # already send to myself
msg.mkreply()
return msg # send reply to inactive conns
def handle_do(self, conn, msg):
def handle_do(self, conn, specifier, data):
# XXX: should this be done asyncron? we could just return the reply in
# that case
if not msg.args:
msg.args = []
# try to actually execute command
msg.set_result(*self._execute_command(msg.module, msg.command, msg.args))
modulename, cmd = specifier.split(u':', 1)
return (COMMANDREPLY, specifier, list(self._execute_command(modulename, cmd, data)))
#if conn in self._active_connections:
# return None # already send to myself
#if conn in self._subscriptions.get(msg.module, set()):
# return None # already send to myself
msg.mkreply()
return msg # send reply to inactive conns
def handle_ping(self, conn, specifier, data):
if data:
raise ProtocolError('poll request don\'t take data!')
return (HEARTBEATREPLY, specifier, [None, {u't':currenttime()}])
def handle_ping(self, conn, msg):
msg.setvalue(u'data', {u't':currenttime()})
msg.mkreply()
return msg
def handle_activate(self, conn, msg):
if msg.module:
if msg.module not in self._modules:
raise NoSuchModuleError()
def handle_activate(self, conn, specifier, data):
if data:
raise ProtocolError('activate request don\'t take data!')
if specifier:
modulename, pname = specifier, None
if ':' in specifier:
modulename, pname = specifier.split(u':', 1)
if modulename not in self._export:
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
if pname and pname not in self.get_module(modulename).accessibles:
# what if we try to subscribe a command here ???
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
# activate only ONE module
self.subscribe(conn, msg.specifier, u'')
modules = [msg.specifier]
self.subscribe(conn, modulename, pname)
modules = [(modulename, pname)]
else:
# activate all modules
self._active_connections.add(conn)
modules = self._modules
modules = [(m, None) for m in self._export]
# send updates for all values. The first poll already happend before the server is active
for modulename in modules:
# send updates for all subscribed values.
# note: The initial poll already happend before the server is active
for modulename, pname in modules:
moduleobj = self._modules.get(modulename, None)
if moduleobj is None:
self.log.error(u'activate: can not lookup module %r, skipping it' % modulename)
if pname:
pobj = moduleobj.accessibles[pname]
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pname),
[pobj.export_value(), dict(t=pobj.timestamp)])
conn.queue_async_reply(updmsg)
continue
for pname, pobj in moduleobj.accessibles.items():
for pname, pobj in moduleobj.accessibles.items(): # pylint: disable=redefined-outer-name
if not isinstance(pobj, Parameter):
continue
if not pobj.export: # XXX: handle export_as cases!
continue
# can not use announce_update here, as this will send to all clients
updmsg = Message(EVENTREPLY, module=moduleobj.name, parameter=pname)
updmsg.set_result(pobj.export_value(), dict(t=pobj.timestamp))
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pname),
[pobj.export_value(), dict(t=pobj.timestamp)])
conn.queue_async_reply(updmsg)
msg.mkreply()
conn.queue_async_reply(msg) # should be sent AFTER all the ^^initial updates
return None
return (ENABLEEVENTSREPLY, specifier, None) if specifier else (ENABLEEVENTSREPLY, None, None)
def handle_deactivate(self, conn, msg):
if msg.specifier:
self.unsubscribe(conn, msg.specifier, u'')
def handle_deactivate(self, conn, specifier, data):
if specifier:
self.unsubscribe(conn, specifier)
else:
self._active_connections.discard(conn)
# XXX: also check all entries in self._subscriptions?
msg.mkreply()
return msg
def handle_error(self, conn, msg):
# is already an error-reply (came from interface frontend) -> just send it back
return msg
return (DISABLEEVENTSREPLY, None, None)

View File

@ -19,12 +19,47 @@
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
#
# *****************************************************************************
"""provide server interfaces to be used by clients"""
from __future__ import absolute_import
from .tcp import TCPServer
import json
INTERFACES = {'tcp': TCPServer, }
EOL = b'\n'
SPACE = b' '
# for 'from protocol.interface import *' to only import the dict
__ALL__ = ['INTERFACES']
def encode_msg_frame(action, specifier=None, data=None):
""" encode a msg_tripel into an msg_frame, ready to be sent
action (and optional specifier) are unicode strings,
data may be an json-yfied python object"""
action = action.encode('utf-8')
if specifier is None:
# implicit: data is None
return b''.join((action, EOL))
specifier = specifier.encode('utf-8')
if data:
data = json.dumps(data).encode('utf-8')
return b''.join((action, SPACE, specifier, SPACE, data, EOL))
return b''.join((action, SPACE, specifier, EOL))
def get_msg(_bytes):
"""try to deframe the next msg in (binary) input
always return a tupel (msg, remaining_input)
msg may also be None
"""
if EOL not in _bytes:
return None, _bytes
return _bytes.split(EOL, 1)
def decode_msg(msg):
"""decode the (binary) msg into a (unicode) msg_tripel"""
# check for leading/trailing CR and remove it
res = msg.split(b' ', 2)
action = res[0].decode('utf-8')
if len(res) == 1:
return action, None, None
specifier = res[1].decode('utf-8')
if len(res) == 2:
return action, specifier, None
data = json.loads(res[2].decode('utf-8'))
return action, specifier, data

View File

@ -30,61 +30,19 @@ except ImportError:
import SocketServer as socketserver # py2
from secop.lib import formatExtendedStack, formatException
from secop.protocol.messages import HELPREPLY, Message, HelpMessage
from secop.protocol.messages import HELPREQUEST, HELPREPLY, HelpMessage
from secop.errors import SECoPError
from secop.protocol.interface import encode_msg_frame, get_msg, decode_msg
DEF_PORT = 10767
MAX_MESSAGE_SIZE = 1024
MESSAGE_READ_SIZE = 1024
EOL = b'\n'
CR = b'\r'
SPACE = b' '
def encode_msg_frame(action, specifier=None, data=None):
""" encode a msg_tripel into an msg_frame, ready to be sent
action (and optional specifier) are unicode strings,
data may be an json-yfied python object"""
action = action.encode('utf-8')
if specifier is None:
# implicit: data is None
return b''.join((action, EOL))
specifier = specifier.encode('utf-8')
if data:
data = data.encode('utf-8')
return b''.join((action, SPACE, specifier, SPACE, data, EOL))
return b''.join((action, SPACE, specifier, EOL))
def get_msg(_bytes):
"""try to deframe the next msg in (binary) input
always return a tupel (msg, remaining_input)
msg may also be None
"""
if EOL not in _bytes:
return None, _bytes
return _bytes.split(EOL, 1)
def decode_msg(msg):
"""decode the (binary) msg into a (unicode) msg_tripel"""
# check for leading/trailing CR and remove it
if msg and msg[0] == CR:
msg = msg[1:]
if msg and msg[-1] == CR:
msg = msg[:-1]
res = msg.split(b' ', 2)
action = res[0].decode('utf-8')
if len(res) == 1:
return action, None, None
specifier = res[1].decode('utf-8')
if len(res) == 2:
return action, specifier, None
data = res[2].decode('utf-8')
return action, specifier, data
class TCPRequestHandler(socketserver.BaseRequestHandler):
@ -118,10 +76,11 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
# put frame(s) into framer to get bytestring
# send bytestring
outmsg = self._queue.popleft()
#outmsg.mkreply()
outdata = encode_msg_frame(*outmsg.serialize())
# outframes = self.encoding.encode(outmsg)
# outdata = self.framing.encode(outframes)
if not outmsg:
outmsg = ('error','InternalError', ['<unknown origin>', 'trying to send none-data', {}])
if len(outmsg) > 3:
outmsg = ('error', 'InternalError', ['<unknown origin>', 'bad message format', {'msg':outmsg}])
outdata = encode_msg_frame(*outmsg)
try:
mysocket.sendall(outdata)
except Exception:
@ -129,7 +88,7 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
# XXX: improve: use polling/select here?
try:
newdata = mysocket.recv(MAX_MESSAGE_SIZE)
newdata = mysocket.recv(MESSAGE_READ_SIZE)
if not newdata:
# no timeout error, but no new data -> connection closed
return
@ -150,33 +109,39 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
origin, data = get_msg(data)
if origin is None:
break # no more messages to process
if not origin: # empty string -> send help message
origin = origin.strip()
if origin and origin[0] == CR:
origin = origin[1:]
if origin and origin[-1] == CR:
origin = origin[:-1]
if origin in (HELPREQUEST, ''): # empty string -> send help message
for idx, line in enumerate(HelpMessage.splitlines()):
msg = Message(HELPREPLY, specifier='%d' % idx)
msg.data = line
self.queue_async_reply(msg)
self.queue_async_reply((HELPREPLY, '%d' % (idx+1), line))
continue
msg = decode_msg(origin)
# construct msgObj from msg
result = None
try:
msgObj = Message(*msg)
msgObj.origin = origin.decode('latin-1')
msgObj = serverobj.dispatcher.handle_request(self, msgObj)
result = serverobj.dispatcher.handle_request(self, msg)
if (msg[0] == 'read') and result:
# read should only trigger async_replies
self.queue_async_reply(('error', 'InternalError', [origin,
'read should only trigger async data units']))
except SECoPError as err:
msgObj.set_error(err.name, str(err), {'exception': formatException(),
'traceback': formatExtendedStack()})
result = ('error', err.name, [origin, str(err), {'exception': formatException(),
'traceback': formatExtendedStack()}])
except Exception as err:
# create Error Obj instead
msgObj.set_error(u'Internal', str(err), {'exception': formatException(),
'traceback':formatExtendedStack()})
result = ('error', 'InternalError', [origin, str(err), {'exception': formatException(),
'traceback': formatExtendedStack()}])
print('--------------------')
print(formatException())
print('--------------------')
print(formatExtendedStack())
print('====================')
if msgObj:
self.queue_reply(msgObj)
if not result:
self.log.error('empty result upon msg %s' % repr(msg))
self.queue_async_reply(result)
def queue_async_reply(self, data):
"""called by dispatcher for async data units"""
@ -185,14 +150,6 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
else:
self.log.error('should async_queue empty data!')
def queue_reply(self, data):
"""called by dispatcher to queue (sync) replies"""
# sync replies go first!
if data:
self._queue.appendleft(data)
else:
self.log.error('should queue empty data!')
def finish(self):
"""called when handle() terminates, i.e. the socket closed"""
self.log.info('closing connection from %s:%d' % self.client_address)
@ -211,16 +168,17 @@ class TCPServer(socketserver.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
def __init__(self, logger, interfaceopts, dispatcher):
self.dispatcher = dispatcher
def __init__(self, name, logger, options, srv):
self.dispatcher =srv.dispatcher
self.name = name
self.log = logger
bindto = interfaceopts.pop('bindto', 'localhost')
portnum = int(interfaceopts.pop('bindport', DEF_PORT))
bindto = options.pop('bindto', 'localhost')
portnum = int(options.pop('bindport', DEF_PORT))
if ':' in bindto:
bindto, _port = bindto.rsplit(':')
portnum = int(_port)
self.log.info("TCPServer binding to %s:%d" % (bindto, portnum))
self.log.info("TCPServer %s binding to %s:%d" % (name, bindto, portnum))
socketserver.ThreadingTCPServer.__init__(
self, (bindto, portnum), TCPRequestHandler, bind_and_activate=True)
self.log.info("TCPServer initiated")

View File

@ -22,9 +22,6 @@
"""Define SECoP Messages"""
from __future__ import print_function
import json
from secop.protocol.errors import EXCEPTIONS
# allowed actions:
IDENTREQUEST = u'*IDN?' # literal
@ -76,120 +73,6 @@ REQUEST2REPLY = {
class Message(object):
"""base class for messages"""
origin = u'<unknown source>'
action = u'<unknown message type>'
specifier = None
data = None
# cooked versions
module = None
parameter = None
command = None
args = None
# if set, these are used for generating the reply
qualifiers = None # will be rectified to dict() in __init__
value = None # also the result of a command
# if set, these are used for generating the error msg
errorclass = '' # -> specifier
errordescription = '' # -> data[1] (data[0] is origin)
errorinfo = {} # -> data[2]
def __init__(self, action, specifier=None, data=None, **kwds):
self.qualifiers = {}
self.action = action
if data:
data = json.loads(data)
if specifier:
self.module = specifier
self.specifier = specifier
if ':' in specifier:
self.module, p = specifier.split(':',1)
if action in (COMMANDREQUEST, COMMANDREPLY):
self.command = p
# XXX: extract args?
self.args = data
else:
self.parameter = p
if data is not None:
self.data = data
elif data is not None:
self.data = data
# record extra values
self.__arguments = set()
for k, v in kwds.items():
self.setvalue(k, v)
def setvalue(self, key, value):
setattr(self, key, value)
self.__arguments.add(key)
def setqualifier(self, key, value):
self.qualifiers[key] = value
def __repr__(self):
return u'Message(%r' % self.action + \
u', '.join('%s=%s' % (k, repr(getattr(self, k)))
for k in sorted(self.__arguments)) + u')'
def serialize(self):
"""return <action>,<specifier>,<jsonyfied_data> triple"""
if self.errorclass:
for k in self.__arguments:
if k in (u'origin', u'errorclass', u'errorinfo', u'errordescription'):
if k in self.errorinfo:
del self.errorinfo[k]
continue
self.errorinfo[k] = getattr(self, k)
data = [self.origin, self.errordescription, self.errorinfo]
print(repr(data))
return ERRORREPLY, self.errorclass, json.dumps(data)
elif self.value or self.qualifiers:
data = [self.value, self.qualifiers]
else:
data = self.data
try:
data = json.dumps(data) if data else u''
except TypeError:
print('Can not serialze: %s' % repr(data))
data = u'none'
if self.specifier:
specifier = self.specifier
else:
specifier = self.module
if self.parameter:
specifier = u'%s:%s' %(self.module, self.parameter)
if self.command:
specifier = u'%s:%s' %(self.module, self.command)
return self.action, specifier, data
def mkreply(self):
self.action = REQUEST2REPLY.get(self.action, self.action)
def set_error(self, errorclass, errordescription, errorinfo):
if errorclass not in EXCEPTIONS:
errordescription = '%s is not an official errorclass!\n%s' % (errorclass, errordescription)
errorclass = u'Internal'
# used to mark thes as an error message
# XXX: check errorclass for allowed values !
self.setvalue(u'errorclass', errorclass) # a str
self.setvalue(u'errordescription', errordescription) # a str
self.setvalue(u'errorinfo', errorinfo) # a dict
self.action = ERRORREPLY
def set_result(self, value, qualifiers):
# used to mark thes as an result reply message
self.setvalue(u'value', value)
self.qualifiers.update(qualifiers)
self.__arguments.add(u'qualifier')
HelpMessage = u"""Try one of the following:
'%s' to query protocol version
'%s' to read the description