mirror of
git://git.psyced.org/git/pypsyc
synced 2024-08-15 03:20:04 +00:00
156 lines
5.7 KiB
Python
156 lines
5.7 KiB
Python
# TODO: write tests
|
|
from pypsyc import MMPVARS
|
|
|
|
from copy import deepcopy
|
|
|
|
class SenderState:
|
|
def __init__(self):
|
|
self.laststate = {}
|
|
self.persistent_out = {}
|
|
def serializeList(self, varname, value):
|
|
t = []
|
|
old = self.persistent_out.get(varname)
|
|
# that is far to experimental
|
|
if True: # not old: # no previous list sent
|
|
self.persistent_out[varname] = value
|
|
t.append(':%s\t%s\n'%(varname, value[0].replace('\n', '\n\t')))
|
|
for item in value[1:]:
|
|
t.append(':\t%s\n'%(item.replace('\n', '\n\t')))
|
|
|
|
return '\n'.join(t)
|
|
if False:
|
|
augmented = filter(lambda x: x not in old, value)
|
|
if augmented:
|
|
packet += '+%s\t%s\n'%(varname, augmented[0].replace('\n',
|
|
'\n\t'))
|
|
for item in augmented[1:]:
|
|
packet += '+\t%s\n'%(item.replace('\n', '\n\t'))
|
|
diminished = filter(lambda x: x not in value, old)
|
|
if diminished:
|
|
packet += '-%s\t%s\n'%(varname, diminished[0].replace('\n',
|
|
'\n\t'))
|
|
for item in diminished[1:]:
|
|
packet += '-\t%s\n'%(item.replace('\n', '\n\t'))
|
|
self.persistent_out[varname] = value
|
|
def serialize(self, state):
|
|
"""
|
|
serializes a set of variables into a string
|
|
|
|
@type state: C{dict}
|
|
@param state: Dictionary of variables to be serialized
|
|
"""
|
|
L = []
|
|
# beware of the lambdas!
|
|
L.append(self.varencode(filter(lambda x: x[0] in MMPVARS and x[1],
|
|
state.items())))
|
|
if L != []:
|
|
L.append('\n')
|
|
L.append(self.varencode(filter(lambda x: x[0] not in MMPVARS and x[1],
|
|
state.items())))
|
|
self.laststate = state
|
|
bytes = '\n'.join(L)
|
|
return bytes
|
|
def varencode(self, v):
|
|
"""
|
|
encodes a set of variables, setting the state persistent according to
|
|
some strategy
|
|
|
|
@type v: C{dict}
|
|
@param v: Dictionary of variables to be serialized
|
|
"""
|
|
t = []
|
|
for (varname, value) in v:
|
|
if self.persistent_out.get(varname) == value:
|
|
pass
|
|
elif varname.startswith('_list') or type(value) == type([]):
|
|
t.append(self.serializeList(varname, value))
|
|
elif self.laststate.get(varname) == value and varname != '_context':
|
|
self.persistent_out[varname] = value
|
|
t.append('=%s\t%s\n'%(varname, value.replace('\n', '\n\t')))
|
|
else:
|
|
t.append(':%s\t%s\n'%(varname, value.replace('\n', '\n\t')))
|
|
return '\n'.join(t)
|
|
|
|
|
|
class ReceiverState:
|
|
glyph = ''
|
|
varname = ''
|
|
listFlag = False
|
|
value = ''
|
|
def __init__(self):
|
|
self.state = {}
|
|
self.persistent = {}
|
|
def reset(self):
|
|
self.state = {}
|
|
self.glyph = ''
|
|
self.varname = ''
|
|
self.listFlag= False
|
|
self.value = ''
|
|
def copy(self):
|
|
# do we actually need those deep copys? TODO
|
|
t = deepcopy(self.persistent)
|
|
t.update(deepcopy(self.state))
|
|
return t
|
|
def eat(self, line):
|
|
"""
|
|
this one is tricky... first it handles the previous line,
|
|
and then it prepares the current line.
|
|
This is needed to implement multiline-continuations in lists
|
|
|
|
@type line: C{str} or C{None}
|
|
@param line: line to be parsed, a None signals that variable
|
|
parsing for current packet is finished
|
|
"""
|
|
if line: line = line.decode('iso-8859-1') # we use unicode internally
|
|
if line and (line[0] == ' ' or line[0] == '\t'): # multiline support
|
|
self.value += '\n' + line[1:]
|
|
return
|
|
|
|
# glyph handling
|
|
if self.glyph == ':':
|
|
if self.listFlag:
|
|
if not type(self.state[self.varname]) == list:
|
|
self.state[self.varname] = [self.state[self.varname]]
|
|
self.state[self.varname].append(self.value)
|
|
else:
|
|
if self.varname.startswith('_list'):
|
|
self.value = [self.value]
|
|
self.state[self.varname] = self.value
|
|
elif self.glyph == '=':
|
|
if self.listFlag:
|
|
if not type(self.persistent[self.varname]) == list:
|
|
self.persistent[self.varname] = [self.self.persistent[self.varname]]
|
|
self.persistent[self.varname].append(self.value)
|
|
else:
|
|
self.persistent[self.varname] = self.value
|
|
elif self.glyph == '+':
|
|
self.persistent.get(self.varname, []).append(self.value)
|
|
elif self.glyph == '-':
|
|
raise NotImplementedError
|
|
elif self.glyph == '?':
|
|
raise NotImplementedError
|
|
|
|
if not line: # feeding done
|
|
return
|
|
|
|
# here we parse the current line
|
|
self.glyph = line[0]
|
|
if line[1] == '\t':
|
|
# lastvarname-optimization: varname remains the same
|
|
self.listFlag = True
|
|
self.value = line[2:]
|
|
else:
|
|
self.listFlag = False
|
|
if line.find('\t') == -1:
|
|
self.varname = line[1:]
|
|
self.value = ''
|
|
else:
|
|
self.varname, self.value = line[1:].split('\t', 1)
|
|
self.value = self.value
|
|
|
|
|
|
class State(SenderState, ReceiverState):
|
|
"""combination of sender and receiver state"""
|
|
def __init__(self):
|
|
SenderState.__init__(self)
|
|
ReceiverState.__init__(self)
|