""" pypsyc.core.mmp ~~~~~~~~~~~~~~~ Classes which are related to the routing layer (called MMP). :copyright: 2010 by Manuel Jacob :license: MIT """ from cStringIO import StringIO from twisted.internet.tcp import BaseClient from twisted.protocols.basic import LineReceiver class Uni(str): """ This class provides some helpers for working with PSYC Uniforms. It can be initialized with a string or a list (:meth:`from_parts`). """ def __init__(self, uni): str.__init__(uni) self.prefix = uni.split(':', 1)[0] def into_parts(self): tmp = self.split(':', 1)[1] return tmp.strip('/').split('/') @classmethod def from_parts(cls, parts, prefix='psyc'): """ Construct a new UNI object from a list of parts. A prefix other than 'psyc' can be given optionally. :param parts: the parts which represent the UNI's path :type parts: list of strings :param prefix: prefix of the UNI :type prefix: string """ uni = str.__new__(cls, '%s://%s' % (prefix, '/'.join(parts))) uni.prefix = prefix return uni def chain(self, child): return Uni('/'.join((self.strip('/'), child))) def is_descendant_of(self, other): """Return whether this uni is a descendant of another.""" other = other.strip('/') l = len(other) return self[0:l] == other and self[l] == '/' def is_ancestor_of(self, other): """Return whether this uni is an ancestor of another.""" self_ = self.strip('/') l = len(self_) return other[0:l] == self_ and other[l] == '/' class Header(dict): """ A class which provides simple access to the header of a PSYC Packet. .. attribute:: source '_source' variable of the packet header as :class:`Uni` .. attribute:: target '_target' variable of the packet header as :class:`Uni` .. attribute:: context '_context' variable of the packet header as :class:`Uni` """ def _init(self): """Initialize the header with the set values.""" source = self.get('_source') if source: self.source = Uni(source) else: self.source = None target = self.get('_target') if target: self.target = Uni(target) else: self.target = None context = self.get('_context') if context: self.context = Uni(context) else: self.context = None class Circuit(LineReceiver, object): """Base class for all PSYC (MMP) Circuits.""" delimiter = '\n' def __init__(self): # states: # 0 = uninitialized # 1 = header # 2 = content self.state = 0 self.vars = {'=': {}} self.inited = None def connectionMade(self): if not hasattr(self, 'initiator'): self.initiator = isinstance(self.transport, BaseClient) if self.initiator: self._init() def _init(self): self.send({}, '') if callable(self.inited): self.inited() del self.inited del self.initiator def lineReceived(self, line): if line == '|': # end of packet if self.state != 0: # the connection is inited already header = Header(self.vars['=']) header.update(self.vars[':']) header._init() self.packet_received(header, self.content) elif not getattr(self, 'initiator', True): self._init() # reset parser state self.state = 1 self.vars[':'] = {} self.content = [] elif self.state == 1: if line == '': self.state = 2 else: self.vars[line[0]].__setitem__(*line[1:].split('\t', 1)) elif self.state == 2: self.content.append(line) def packet_received(self, header, content): """Overide this in subclass.""" raise NotImplementedError def send(self, header, content): """Send a packet over this circuit.""" out = StringIO() out.writelines(':%s\t%s\n' % x for x in header.iteritems()) content = '\n'.join(content) if content: out.write('\n') out.write(content) out.write('\n') out.write('|\n') self.transport.write(out.getvalue())