Fixed and refactored OOB system and tested with new websocket client

This commit is contained in:
Griatch 2014-06-28 17:38:21 +02:00
parent 9ba212c264
commit c60a5fdea1
10 changed files with 209 additions and 128 deletions

View file

@ -144,9 +144,9 @@ class ObjectDB(TypedObject):
# make sure to sync the contents cache when initializing # make sure to sync the contents cache when initializing
#_GA(self, "contents_update")() #_GA(self, "contents_update")()
def _at_db_player_presave(self): def _at_db_player_postsave(self):
""" """
This hook is called automatically just before the player field is saved. This hook is called automatically after the player field is saved.
""" """
# we need to re-cache this for superusers to bypass. # we need to re-cache this for superusers to bypass.
self.locks.cache_lock_bypass(self) self.locks.cache_lock_bypass(self)

View file

@ -80,28 +80,28 @@ def hashid(obj, suffix=""):
#------------------------------------------------------------ #------------------------------------------------------------
# callback to field pre_save signal (connected in src.server.server) # callback to field pre_save signal (connected in src.server.server)
def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs): #def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs):
""" # """
Called at the beginning of the field save operation. The save method # Called at the beginning of the field save operation. The save method
must be called with the update_fields keyword in order to be most efficient. # must be called with the update_fields keyword in order to be most efficient.
This method should NOT save; rather it is the save() that triggers this # This method should NOT save; rather it is the save() that triggers this
function. Its main purpose is to allow to plug-in a save handler and oob # function. Its main purpose is to allow to plug-in a save handler and oob
handlers. # handlers.
""" # """
if raw: # if raw:
return # return
if update_fields: # if update_fields:
# this is a list of strings at this point. We want field objects # # this is a list of strings at this point. We want field objects
update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) # update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields)
else: # else:
# meta.fields are already field objects; get them all # # meta.fields are already field objects; get them all
update_fields = _GA(_GA(instance, "_meta"), "fields") # update_fields = _GA(_GA(instance, "_meta"), "fields")
for field in update_fields: # for field in update_fields:
fieldname = field.name # fieldname = field.name
handlername = "_at_%s_presave" % fieldname # handlername = "_at_%s_presave" % fieldname
handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None # handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None
if callable(handler): # if callable(handler):
handler() # handler()
def field_post_save(sender, instance=None, update_fields=None, raw=False, **kwargs): def field_post_save(sender, instance=None, update_fields=None, raw=False, **kwargs):

View file

@ -18,14 +18,19 @@ instance and session is the active session to get return data.
The function names are not case-sensitive (this allows for names The function names are not case-sensitive (this allows for names
like "LIST" which would otherwise collide with Python builtins). like "LIST" which would otherwise collide with Python builtins).
A function named _OOB_ERROR will retrieve error strings if it is A function named OOB_ERROR will retrieve error strings if it is
defined. It will get the error message as its 3rd argument. defined. It will get the error message as its 3rd argument.
Data is usually returned via
session.msg(oob=(cmdname, (args,), {kwargs}))
Note that args, kwargs must be iterable/dict, non-iterables will
be interpreted as a new command name.
""" """
from django.conf import settings from django.conf import settings
_GA = object.__getattribute__ _GA = object.__getattribute__
_SA = object.__setattr__ _SA = object.__setattr__
_NA_REPORT = lambda o: (None, "N/A")
_NA_SEND = lambda o: "N/A" _NA_SEND = lambda o: "N/A"
#------------------------------------------------------------ #------------------------------------------------------------
@ -33,26 +38,25 @@ _NA_SEND = lambda o: "N/A"
# cmdname(oobhandler, session, *args, **kwargs) # cmdname(oobhandler, session, *args, **kwargs)
#------------------------------------------------------------ #------------------------------------------------------------
def _OOB_ERROR(oobhandler, session, errmsg, *args, **kwargs): def OOB_ERROR(oobhandler, session, errmsg, *args, **kwargs):
""" """
A function with this name is special and is called by the oobhandler when an error A function with this name is special and is called by the oobhandler when an error
occurs already at the execution stage (such as the oob function occurs already at the execution stage (such as the oob function
not being recognized or having the wrong args etc). not being recognized or having the wrong args etc).
""" """
session.msg(oob=("send", {"ERROR": errmsg})) session.msg(oob=("err", ("ERROR " + errmsg,)))
def ECHO(oobhandler, session, *args, **kwargs): def ECHO(oobhandler, session, *args, **kwargs):
"Test/debug function, simply returning the args and kwargs" "Test/debug function, simply returning the args and kwargs"
session.msg(oob=("echo", args, kwargs)) session.msg(oob=("echo", args, kwargs))
##OOB{"SEND":"CHARACTER_NAME"}
def SEND(oobhandler, session, *args, **kwargs): def SEND(oobhandler, session, *args, **kwargs):
""" """
This function directly returns the value of the given variable to the This function directly returns the value of the given variable to the
session. session.
""" """
print "In SEND:", oobhandler, session, args
obj = session.get_puppet_or_player() obj = session.get_puppet_or_player()
ret = {} ret = {}
if obj: if obj:
@ -62,14 +66,18 @@ def SEND(oobhandler, session, *args, **kwargs):
ret[name] = value ret[name] = value
except Exception, e: except Exception, e:
ret[name] = str(e) ret[name] = str(e)
# return result session.msg(oob=("send", ret))
session.msg(oob=("send", ret)) else:
session.msg(oob=("err", ("You must log in first.",)))
##OOB{"REPORT":"TEST"}
def REPORT(oobhandler, session, *args, **kwargs): def REPORT(oobhandler, session, *args, **kwargs):
""" """
This creates a tracker instance to track the data given in *args. This creates a tracker instance to track the data given in *args.
The tracker will return with a oob structure
oob={"report":["attrfieldname", (args,), {kwargs}}
Note that the data name is assumed to be a field is it starts with db_* Note that the data name is assumed to be a field is it starts with db_*
and an Attribute otherwise. and an Attribute otherwise.
@ -79,27 +87,37 @@ def REPORT(oobhandler, session, *args, **kwargs):
obj = session.get_puppet_or_player() obj = session.get_puppet_or_player()
if obj: if obj:
for name in (a.upper() for a in args if a): for name in (a.upper() for a in args if a):
typ, val = OOB_REPORTABLE.get(name, _NA_REPORT)(obj) trackname = OOB_REPORTABLE.get(name, None)
if typ == "field": if not trackname:
oobhandler.track_field(obj, session.sessid, name) session.msg(oob=("err", ("No Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % trackname,)))
elif typ == "attribute": elif trackname.startswith("db_"):
oobhandler.track_attribute(obj, session.sessid, name) oobhandler.track_field(obj, session.sessid, trackname)
else:
oobhandler.track_attribute(obj, session.sessid, trackname)
else:
session.msg(oob=("err", ("You must log in first.",)))
def UNREPORT(oobhandler, session, vartype="prop", *args, **kwargs): ##OOB{"UNREPORT": "TEST"}
def UNREPORT(oobhandler, session, *args, **kwargs):
""" """
This removes tracking for the given data given in *args. This removes tracking for the given data given in *args.
""" """
obj = session.get_puppet_or_player() obj = session.get_puppet_or_player()
if obj: if obj:
for name in (a.upper() for a in args if a): for name in (a.upper() for a in args if a):
typ, val = OOB_REPORTABLE.get(name, _NA_REPORT) trackname = OOB_REPORTABLE.get(name, None)
if typ == "field": if not trackname:
oobhandler.untrack_field(obj, session.sessid, name) session.msg(oob=("err", ("No Un-Reportable property '%s'. Use LIST REPORTED_VALUES." % name,)))
elif trackname.startswith("db_"):
oobhandler.untrack_field(obj, session.sessid, trackname)
else: # assume attribute else: # assume attribute
oobhandler.untrack_attribute(obj, session.sessid, name) oobhandler.untrack_attribute(obj, session.sessid, trackname)
else:
session.msg(oob=("err", ("You must log in first.",)))
##OOB{"LIST":"COMMANDS"}
def LIST(oobhandler, session, mode, *args, **kwargs): def LIST(oobhandler, session, mode, *args, **kwargs):
""" """
List available properties. Mode is the type of information List available properties. Mode is the type of information
@ -135,15 +153,42 @@ def LIST(oobhandler, session, mode, *args, **kwargs):
session.msg(oob=("list", ("REPORTABLE_VARIABLES",) + session.msg(oob=("list", ("REPORTABLE_VARIABLES",) +
tuple(key for key in OOB_REPORTABLE.keys()))) tuple(key for key in OOB_REPORTABLE.keys())))
elif mode == "REPORTED_VARIABLES": elif mode == "REPORTED_VARIABLES":
session.msg(oob=("list", ("REPORTED_VARIABLES",) + # we need to check so as to use the right return value depending on if it is
tuple(oobhandler.get_all_tracked(session)))) # an Attribute (identified by tracking the db_value field) or a normal database field
reported = oobhandler.get_all_tracked(session)
reported = [stored[2] if stored[2] != "db_value" else stored[4][0] for stored in reported]
session.msg(oob=("list", ["REPORTED_VARIABLES"] + reported))
elif mode == "SENDABLE_VARIABLES": elif mode == "SENDABLE_VARIABLES":
session.msg(oob=("list", ("SENDABLE_VARIABLES",) + session.msg(oob=("list", ("SENDABLE_VARIABLES",) +
tuple(key for key in OOB_REPORTABLE.keys()))) tuple(key for key in OOB_REPORTABLE.keys())))
#elif mode == "CONFIGURABLE_VARIABLES": elif mode == "CONFIGURABLE_VARIABLES":
# pass # Not implemented (game specific)
pass
else: else:
session.msg(oob=("list", ("unsupported mode",))) session.msg(oob=("err", ("LIST", "Unsupported mode",)))
def _repeat_callback(oobhandler, session, *args, **kwargs):
"Set up by REPEAT"
session.msg(oob=("repeat", ("Repeat!",)))
##OOB{"REPEAT":10}
def REPEAT(oobhandler, session, interval, *args, **kwargs):
"""
Test command for the repeat functionality. Note that the args/kwargs
must not be db objects (or anything else non-picklable), rather use
dbrefs if so needed. The callback must be defined globally and
will be called as
callback(oobhandler, session, *args, **kwargs)
"""
oobhandler.repeat(None, session.sessid, interval, _repeat_callback, *args, **kwargs)
##OOB{"UNREPEAT":10}
def UNREPEAT(oobhandler, session, interval):
"""
Disable repeating callback
"""
oobhandler.unrepeat(None, session.sessid, interval)
# Mapping for how to retrieve each property name. # Mapping for how to retrieve each property name.
@ -167,9 +212,10 @@ OOB_SENDABLE = {
"UTF_8": lambda o: True "UTF_8": lambda o: True
} }
# mapping for which properties may be tracked. Each callable should return a tuple (type, value) where # mapping for which properties may be tracked. Each value points either to a database field
# the type is one of "field" or "attribute" depending on what is being tracked. # (starting with db_*) or an Attribute name.
OOB_REPORTABLE = { OOB_REPORTABLE = {
"CHARACTER_NAME": lambda o: ("field", o.key), "CHARACTER_NAME": "db_key",
"ROOM_NAME": lambda o: ("attribute", o.db_location.key) "ROOM_NAME": "db_location",
"TEST" : "test"
} }

View file

@ -36,7 +36,6 @@ messages.
from inspect import isfunction from inspect import isfunction
from twisted.internet.defer import inlineCallbacks from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
from django.conf import settings from django.conf import settings
from src.server.models import ServerConfig from src.server.models import ServerConfig
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
@ -57,12 +56,12 @@ for mod in make_iter(settings.OOB_PLUGIN_MODULES):
_OOB_FUNCS.update(dict((key.lower(), func) for key, func in all_from_module(mod).items() if isfunction(func))) _OOB_FUNCS.update(dict((key.lower(), func) for key, func in all_from_module(mod).items() if isfunction(func)))
# get custom error method or use the default # get custom error method or use the default
_OOB_ERROR = _OOB_FUNCS.get("_OOB_ERROR", None) _OOB_ERROR = _OOB_FUNCS.get("oob_error", None)
if not _OOB_ERROR: if not _OOB_ERROR:
# create default oob error message function # create default oob error message function
def oob_error(oobhandler, session, errmsg, *args, **kwargs): def oob_error(oobhandler, session, errmsg, *args, **kwargs):
"Error wrapper" "Error wrapper"
session.msg(oob=("send", {"ERROR": errmsg})) session.msg(oob=("err", ("ERROR ", errmsg)))
_OOB_ERROR = oob_error _OOB_ERROR = oob_error
@ -105,16 +104,16 @@ class TrackerHandler(object):
def remove(self, fieldname, trackerclass, *args, **kwargs): def remove(self, fieldname, trackerclass, *args, **kwargs):
""" """
Remove tracker from handler. Raises KeyError if tracker Remove identified tracker from TrackerHandler.
is not found. Raises KeyError if tracker is not found.
""" """
trackerkey = trackerclass.__name__ trackerkey = trackerclass.__name__
tracker = self.tracktargets[fieldname][trackerkey] tracker = self.tracktargets[fieldname][trackerkey]
try: try:
tracker.at_delete(*args, **kwargs) tracker.at_remove(*args, **kwargs)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
del tracker del self.tracktargets[fieldname][trackerkey]
self.ntrackers -= 1 self.ntrackers -= 1
if self.ntrackers <= 0: if self.ntrackers <= 0:
# if there are no more trackers, clean this handler # if there are no more trackers, clean this handler
@ -173,9 +172,9 @@ class ReportFieldTracker(TrackerBase):
new_value = new_value.key new_value = new_value.key
except AttributeError: except AttributeError:
new_value = to_str(new_value, force_string=True) new_value = to_str(new_value, force_string=True)
kwargs[self.fieldname] = new_value
# this is a wrapper call for sending oob data back to session # this is a wrapper call for sending oob data back to session
self.oobhandler.msg(self.sessid, "report", self.fieldname, self.oobhandler.msg(self.sessid, "report", *args, **kwargs)
new_value, *args, **kwargs)
class ReportAttributeTracker(TrackerBase): class ReportAttributeTracker(TrackerBase):
@ -199,8 +198,9 @@ class ReportAttributeTracker(TrackerBase):
new_value = new_value.dbobj new_value = new_value.dbobj
except AttributeError: except AttributeError:
new_value = to_str(new_value, force_string=True) new_value = to_str(new_value, force_string=True)
kwargs[self.attrname] = new_value
# this is a wrapper call for sending oob data back to session # this is a wrapper call for sending oob data back to session
self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs) self.oobhandler.msg(self.sessid, "report", *args, **kwargs)
@ -208,25 +208,21 @@ class ReportAttributeTracker(TrackerBase):
class OOBTicker(Ticker): class OOBTicker(Ticker):
""" """
Version of Ticker that calls OOB_FUNC rather than trying to call Version of Ticker that executes an executable rather than trying to call
a hook method. a hook method.
""" """
@inlineCallbacks @inlineCallbacks
def _callback(self, oobhandler, sessions): def _callback(self):
"See original for more info" "See original for more info"
for key, (_, args, kwargs) in self.subscriptions.items(): for key, (_, args, kwargs) in self.subscriptions.items():
session = sessions.session_from_sessid(kwargs.get("sessid")) # args = (sessid, callback_function)
session = SESSIONS.session_from_sessid(args[0])
try: try:
oobhandler.execute_cmd(session, kwargs.get("func_key"), *args, **kwargs) # execute the oob callback
yield args[1](OOB_HANDLER, session, *args[2:], **kwargs)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
def __init__(self, interval):
"Sets up the Ticker"
self.interval = interval
self.subscriptions = {}
self.task = LoopingCall(self._callback, OOB_HANDLER, SESSIONS)
class OOBTickerPool(TickerPool): class OOBTickerPool(TickerPool):
ticker_class = OOBTicker ticker_class = OOBTicker
@ -270,9 +266,9 @@ class OOBHandler(object):
tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage") tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage")
if tracker_storage: if tracker_storage:
self.oob_tracker_storage = dbunserialize(tracker_storage) self.oob_tracker_storage = dbunserialize(tracker_storage)
#print "recovered from tracker_storage:", self.oob_tracker_storage
for (obj, sessid, fieldname, trackerclass, args, kwargs) in self.oob_tracker_storage.values(): for (obj, sessid, fieldname, trackerclass, args, kwargs) in self.oob_tracker_storage.values():
self.track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs) #print "restoring tracking:",obj, sessid, fieldname, trackerclass
self._track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs)
# make sure to purge the storage # make sure to purge the storage
ServerConfig.objects.conf(key="oob_tracker_storage", delete=True) ServerConfig.objects.conf(key="oob_tracker_storage", delete=True)
self.tickerhandler.restore() self.tickerhandler.restore()
@ -302,6 +298,7 @@ class OOBHandler(object):
storekey = (obj_packed, sessid, propname) storekey = (obj_packed, sessid, propname)
stored = (obj_packed, sessid, propname, trackerclass, args, kwargs) stored = (obj_packed, sessid, propname, trackerclass, args, kwargs)
self.oob_tracker_storage[storekey] = stored self.oob_tracker_storage[storekey] = stored
#print "_track:", obj, id(obj), obj.__dict__
def _untrack(self, obj, sessid, propname, trackerclass, *args, **kwargs): def _untrack(self, obj, sessid, propname, trackerclass, *args, **kwargs):
""" """
@ -312,9 +309,8 @@ class OOBHandler(object):
obj = obj.dbobj obj = obj.dbobj
except AttributeError: except AttributeError:
pass pass
try: try:
# call at_delete hook # call at_remove hook on the trackerclass
_GA(obj, "_trackerhandler").remove(propname, trackerclass, *args, **kwargs) _GA(obj, "_trackerhandler").remove(propname, trackerclass, *args, **kwargs)
except AttributeError: except AttributeError:
pass pass
@ -327,7 +323,7 @@ class OOBHandler(object):
Get the names of all variables this session is tracking. Get the names of all variables this session is tracking.
""" """
sessid = session.sessid sessid = session.sessid
return [key[2].lstrip("db_") for key in self.oob_tracker_storage.keys() if key[1] == sessid] return [stored for key, stored in self.oob_tracker_storage.items() if key[1] == sessid]
def track_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker): def track_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker):
""" """
@ -336,14 +332,14 @@ class OOBHandler(object):
""" """
# all database field names starts with db_* # all database field names starts with db_*
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
self._track(obj, sessid, field_name, trackerclass) self._track(obj, sessid, field_name, trackerclass, field_name)
def untrack_field(self, obj, sessid, field_name): def untrack_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker):
""" """
Shortcut for untracking a database field. Uses OOBTracker by defualt Shortcut for untracking a database field. Uses OOBTracker by defualt
""" """
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
self._untrack(obj, sessid, field_name) self._untrack(obj, sessid, field_name, trackerclass)
def track_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker): def track_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker):
""" """
@ -353,14 +349,15 @@ class OOBHandler(object):
""" """
# get the attribute object if we can # get the attribute object if we can
try: try:
obj = obj.dbobj attrobj = obj.dbobj
except AttributeError: except AttributeError:
pass pass
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True) attrobj = obj.attributes.get(attr_name, return_obj=True)
#print "track_attribute attrobj:", attrobj, id(attrobj)
if attrobj: if attrobj:
self._track(attrobj, sessid, "db_value", trackerclass, attr_name) self._track(attrobj, sessid, "db_value", trackerclass, attr_name)
def untrack_attribute(self, obj, sessid, attr_name, trackerclass): def untrack_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker):
""" """
Shortcut for deactivating tracking for a given attribute. Shortcut for deactivating tracking for a given attribute.
""" """
@ -368,25 +365,24 @@ class OOBHandler(object):
obj = obj.dbobj obj = obj.dbobj
except AttributeError: except AttributeError:
pass pass
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True) attrobj = obj.attributes.get(attr_name, return_obj=True)
if attrobj: if attrobj:
self._untrack(attrobj, sessid, attr_name, trackerclass) self._untrack(attrobj, sessid, "db_value", trackerclass, attr_name)
def repeat(self, obj, sessid, func_key, interval=20, *args, **kwargs): def repeat(self, obj, sessid, interval=20, callback=None, *args, **kwargs):
""" """
Start a repeating action. Every interval seconds, Start a repeating action. Every interval seconds, trigger
the oobfunc corresponding to func_key is called with callback(*args, **kwargs). The callback is called with
args and kwargs. args and kwargs; note that *args and **kwargs may not contain
anything un-picklable (use dbrefs if wanting to use objects).
""" """
if not func_key in _OOB_FUNCS: self.tickerhandler.add(obj, interval, sessid, callback, *args, **kwargs)
raise KeyError("%s is not a valid OOB function name.")
self.tickerhandler.add(self, obj, interval, func_key=func_key, sessid=sessid, *args, **kwargs)
def unrepeat(self, obj, sessid, func_key, interval=20): def unrepeat(self, obj, sessid, interval=20):
""" """
Stop a repeating action Stop a repeating action
""" """
self.tickerhandler.remove(self, obj, interval) self.tickerhandler.remove(obj, interval)
# access method - called from session.msg() # access method - called from session.msg()
@ -396,23 +392,26 @@ class OOBHandler(object):
Retrieve oobfunc from OOB_FUNCS and execute it immediately Retrieve oobfunc from OOB_FUNCS and execute it immediately
using *args and **kwargs using *args and **kwargs
""" """
try: oobfunc = _OOB_FUNCS.get(func_key, None)
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys() if not oobfunc:
oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found # function not found
oobfunc(self, session, *args, **kwargs) errmsg = "OOB Error: function '%s' not recognized." % func_key
except KeyError,e:
errmsg = "OOB Error: function '%s' not recognized: %s" % (func_key, e)
if _OOB_ERROR: if _OOB_ERROR:
_OOB_ERROR(self, session, errmsg, *args, **kwargs) _OOB_ERROR(self, session, errmsg, *args, **kwargs)
logger.log_trace()
else: else:
logger.log_trace(errmsg) logger.log_trace(errmsg)
raise KeyError(errmsg) return
# execute the found function
try:
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
oobfunc(self, session, *args, **kwargs)
except Exception, err: except Exception, err:
errmsg = "OOB Error: Exception in '%s'(%s, %s):\n%s" % (func_key, args, kwargs, err) errmsg = "OOB Error: Exception in '%s'(%s, %s):\n%s" % (func_key, args, kwargs, err)
if _OOB_ERROR: if _OOB_ERROR:
_OOB_ERROR(self, session, errmsg, *args, **kwargs) _OOB_ERROR(self, session, errmsg, *args, **kwargs)
else: logger.log_trace(errmsg)
logger.log_trace(errmsg)
raise Exception(errmsg) raise Exception(errmsg)
def msg(self, sessid, funcname, *args, **kwargs): def msg(self, sessid, funcname, *args, **kwargs):

View file

@ -31,7 +31,7 @@ import json
from twisted.internet.protocol import Protocol from twisted.internet.protocol import Protocol
from src.server.session import Session from src.server.session import Session
from src.utils.logger import log_trace from src.utils.logger import log_trace
from src.utils.utils import to_str from src.utils.utils import to_str, make_iter
from src.utils.text2html import parse_html from src.utils.text2html import parse_html
@ -85,7 +85,8 @@ class WebSocketClient(Protocol, Session):
try: try:
oobdata = json.loads(string) oobdata = json.loads(string)
for (key, args) in oobdata.items(): for (key, args) in oobdata.items():
self.data_in(text=None, oob=(key, args)) #print "oob data in:", (key, args)
self.data_in(text=None, oob=(key, make_iter(args)))
except Exception: except Exception:
log_trace("Websocket malformed OOB request: %s" % string) log_trace("Websocket malformed OOB request: %s" % string)
else: else:
@ -119,6 +120,7 @@ class WebSocketClient(Protocol, Session):
self.sendLine(str(e)) self.sendLine(str(e))
if "oob" in kwargs: if "oob" in kwargs:
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob")) oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob"))
#print "oob data_out:", "OOB" + json.dumps(oobstruct)
self.sendLine("OOB" + json.dumps(oobstruct)) self.sendLine("OOB" + json.dumps(oobstruct))
raw = kwargs.get("raw", False) raw = kwargs.get("raw", False)
nomarkup = kwargs.get("nomarkup", False) nomarkup = kwargs.get("nomarkup", False)

View file

@ -33,9 +33,9 @@ from src.server.sessionhandler import SESSIONS
# setting up server-side field cache # setting up server-side field cache
from django.db.models.signals import post_save from django.db.models.signals import post_save
from src.server.caches import field_pre_save from src.server.caches import field_post_save
#pre_save.connect(field_pre_save, dispatch_uid="fieldcache") #pre_save.connect(field_pre_save, dispatch_uid="fieldcache")
post_save.connect(field_pre_save, dispatch_uid="fieldcache") post_save.connect(field_post_save, dispatch_uid="fieldcache")
#from src.server.caches import post_attr_update #from src.server.caches import post_attr_update
#from django.db.models.signals import m2m_changed #from django.db.models.signals import m2m_changed

View file

@ -206,6 +206,7 @@ class ServerSession(Session):
if not _OOB_HANDLER: if not _OOB_HANDLER:
from src.server.oobhandler import OOB_HANDLER as _OOB_HANDLER from src.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None)) oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None))
#print "session.data_in: oobstruct:",oobstruct
for (funcname, args, kwargs) in oobstruct: for (funcname, args, kwargs) in oobstruct:
if funcname: if funcname:
_OOB_HANDLER.execute_cmd(self, funcname, *args, **kwargs) _OOB_HANDLER.execute_cmd(self, funcname, *args, **kwargs)

View file

@ -136,14 +136,17 @@ class SessionHandler(object):
elif isinstance(oobstruct[1], (tuple, list)): elif isinstance(oobstruct[1], (tuple, list)):
# cmdname, (args,) # cmdname, (args,)
return (oobstruct[0].lower(), list(oobstruct[1]), {}) return (oobstruct[0].lower(), list(oobstruct[1]), {})
else:
# cmdname, cmdname
return ((oobstruct[0].lower(), (), {}), (oobstruct[1].lower(), (), {}))
else: else:
# cmdname, (args,), {kwargs} # cmdname, (args,), {kwargs}
return (oobstruct[0].lower(), list(oobstruct[1]), dict(oobstruct[2])) return (oobstruct[0].lower(), list(oobstruct[1]), dict(oobstruct[2]))
if hasattr(oobstruct, "__iter__"): if hasattr(oobstruct, "__iter__"):
# differentiate between (cmdname, cmdname), # differentiate between (cmdname, cmdname),
# (cmdname, args, kwargs) and ((cmdname,args,kwargs), # (cmdname, (args), {kwargs}) and ((cmdname,(args),{kwargs}),
# (cmdname,args,kwargs), ...) # (cmdname,(args),{kwargs}), ...)
if oobstruct and isinstance(oobstruct[0], basestring): if oobstruct and isinstance(oobstruct[0], basestring):
return (list(_parse(oobstruct)),) return (list(_parse(oobstruct)),)

View file

@ -70,7 +70,7 @@ _DA = object.__delattr__
#------------------------------------------------------------ #------------------------------------------------------------
#class Attribute(SharedMemoryModel): #class Attribute(SharedMemoryModel):
class Attribute(WeakSharedMemoryModel): class Attribute(SharedMemoryModel):
""" """
Abstract django model. Abstract django model.
@ -173,13 +173,6 @@ class Attribute(WeakSharedMemoryModel):
""" """
self.db_value = to_pickle(new_value) self.db_value = to_pickle(new_value)
self.save(update_fields=["db_value"]) self.save(update_fields=["db_value"])
try:
# eventual OOB hook
#self._track_db_value_change.update(self.cached_value)
self._track_db_value_change.update(self.new_value)
except AttributeError:
pass
return
#@value.deleter #@value.deleter
def __value_del(self): def __value_del(self):

View file

@ -20,13 +20,38 @@ var OOB_debug = true
// Custom OOB functions // Custom OOB functions
// functions defined here can be called by name by the server. For // functions defined here can be called by name by the server. For
// example the OOB{"echo":arguments} will trigger a function named // example the OOB{"echo":(args),{kwargs}} will trigger a function named
// echo(arguments). // echo(args, kwargs).
function echo(message) { function echo(args, kwargs) {
// example echo function. // example echo function.
doShow("out", "ECHO return: " + message) } doShow("out", "ECHO return: " + args) }
function list (args, kwargs) {
// show in main window
doShow("out", args) }
function send (args, kwargs) {
// show in main window. SEND returns kwargs {name:value}.
for (sendvalue in kwargs) {
doShow("out", sendvalue + " = " + kwargs[sendvalue]);}
}
function report (args, kwargs) {
// show in main window. REPORT returns kwargs
// {attrfieldname:value}
for (name in kwargs) {
doShow("out", name + " = " + kwargs[name]) }
}
function repeat (args, kwargs) {
// called by repeating oob funcs
doShow("out", args) }
function err (args, kwargs) {
// display error
doShow("err", args) }
// Webclient code // Webclient code
@ -61,15 +86,20 @@ function onMessage(evt) {
var inmsg = evt.data var inmsg = evt.data
if (inmsg.length > 3 && inmsg.substr(0, 3) == "OOB") { if (inmsg.length > 3 && inmsg.substr(0, 3) == "OOB") {
// dynamically call oob methods, if available // dynamically call oob methods, if available
try {var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB } try {
var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB }
catch(err) { catch(err) {
// not JSON packed - a normal text // not JSON packed - a normal text
doShow('out', err + " " + inmsg); doShow('out', inmsg);
return; return;
} }
for (var ind in oobarray) { if (typeof oobarray != "undefined") {
try { window[oobarray[ind][0]](oobarray[ind][1]) } for (var ind in oobarray) {
catch(err) { doShow("err", "Could not execute OOB function " + oobtuple[0] + "(" + oobtuple[1] + ")!") } try {
window[oobarray[ind][0]](oobarray[ind][1], oobarray[ind][2]) }
catch(err) {
doShow("err", "Could not execute js OOB function '" + oobarray[ind][0] + "(" + oobarray[ind][1] + oobarray[ind][2] + ")'") }
}
} }
} }
else { else {
@ -93,8 +123,15 @@ function doSend(){
if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") { if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
// test OOB messaging // test OOB messaging
doShow("out", "OOB input: " + outmsg.slice(5)) try {
doOOB(JSON.parse(outmsg.slice(5))); } doShow("out", "OOB input: " + outmsg.slice(5));
if (outmsg.length == 5) {
doShow("err", "OOB testing syntax: ##OOB{\"cmdname:args, ...}"); }
else {
doOOB(JSON.parse(outmsg.slice(5))); } }
catch(err) {
doShow("err", err) }
}
else { else {
// normal output // normal output
websocket.send(outmsg); } websocket.send(outmsg); }