messages.py (2198B)
1 from pyxp.fields import * 2 3 class MessageBase(type): 4 idx = 0 5 6 def __new__(cls, name, bases, attrs): 7 fields = [] 8 fieldmap = {} 9 for k, v in attrs.items(): 10 if isinstance(v, Field): 11 attrs[k] = None 12 fields.append(v) 13 fieldmap[k] = v 14 v.name = k 15 fields.sort(lambda a, b: cmp(a.id, b.id)) 16 17 new_cls = super(MessageBase, cls).__new__(cls, name, bases, attrs) 18 19 map = getattr(new_cls, 'fieldmap', {}) 20 map.update(fieldmap) 21 new_cls.fields = getattr(new_cls, 'fields', ()) + tuple(fields) 22 new_cls.fieldmap = map 23 for f in fields: 24 f.message = new_cls 25 return new_cls 26 27 class Message(object): 28 __metaclass__ = MessageBase 29 def __init__(self, *args, **kwargs): 30 if args: 31 args = dict(zip((f.name for f in self.fields), args)) 32 args.update(kwargs) 33 kwargs = args; 34 for k, v in kwargs.iteritems(): 35 assert k in self.fieldmap, "Invalid keyword argument" 36 setattr(self, k, v) 37 38 @classmethod 39 def field(cls): 40 class MessageField(Field): 41 def repr(self): 42 return cls.__name__ 43 def unmarshall(self, data, offset): 44 return cls.unmarshall(data, offset) 45 def marshall(self, val): 46 return val.marshall() 47 return MessageField() 48 49 @classmethod 50 def unmarshall(cls, data, offset=0): 51 vals = {} 52 start = offset 53 for field in cls.fields: 54 size, vals[field.name] = field.unmarshall(data, offset) 55 offset += size 56 return offset - start, cls(**vals) 57 def marshall(self): 58 res = [] 59 callbacks = [] 60 for field in self.fields: 61 val = field.marshall(getattr(self, field.name, None)) 62 if callable(val): 63 callbacks.append((val, len(res))) 64 if isinstance(val, list): 65 res += val 66 else: 67 res.append(val) 68 for fn, i in reversed(callbacks): 69 res[i] = fn(res, i) 70 return res 71 72 # vim:se sts=4 sw=4 et: