1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
import os import sys import errno
from struct import pack, unpack
class ErlangTermsMixin(object):
def decode(self, term): version = ord(term[0]) if version != 131: raise ValueError('unknown protocol version: {}'.format(version)) return self._decode_term(term[1:])
def _decode_term(self, term): tag = ord(term[0]) tail = term[1:]
if tag == 97: return ord(tail[:1]), tail[1:] elif tag == 107: length, = unpack('>H', tail[:2]) tail = tail[2:] return [ord(i) for i in tail[:length]], tail[length:] elif tag == 100: length, = unpack('>H', tail[:2]) tail = tail[2:] name = tail[:length] tail = tail[length:] return name, tail elif tag == 104: arity = ord(tail[0]) tail = tail[1:]
lst = [] while arity > 0: term, tail = self._decode_term(tail) lst.append(term) arity -= 1 return tuple(lst), tail raise ValueError('unsupported data tag: {}'.format(tag))
def encode(self, term): encoded_term = self._encode_term(term) return '\x83' + encoded_term
def _encode_term(self, term): if isinstance(term, tuple): arity = len(term) if arity <= 255: header = 'h{:c}'.format(arity) elif arity <= 4294967295: header = pack('>BI', 105, arity) else: raise ValueError('invalid tuple arity') return header + ''.join(self._encode_term(t) for t in term) elif isinstance(term, unicode): try: bytes_data = term.encode('latin1') except UnicodeEncodeError: pass return pack('>BH', 107, len(term)) + bytes_data elif isinstance(term, str): length = len(term) if length > 4294967295: raise ValueError('invalid binary length') return pack('>BI', 109, length) + term elif isinstance(term, (int, long)): if 0 <= term <= 255: return 'a{:c}'.format(term) elif -2147483648 <= term <= 2147483647: return pack('>Bi', 98, term) raise ValueError('invalid integer value') raise ValueError('unsupported data type: {}'.format(term))
class Port(ErlangTermsMixin):
PACK_FORMAT = '>H' PACKET_BYTE = 2
def __init__(self): self.in_d = sys.stdin.fileno() self.out_d = sys.stdout.fileno()
def read(self): data = self._read_data(self.PACKET_BYTE) length, = unpack(self.PACK_FORMAT, data) data = self._read_data(length) return self.decode(data)[0]
def _read_data(self, length): data = '' while len(data) != length: try: buf = os.read(self.in_d, length) except IOError as e: if e.errno == errno.EPIPE: raise EOFError('read error, EPIPE') raise IOError('read error, io error') if not buf: raise EOFError('read error, buffer') data += buf return data
def write(self, message): data = self.encode(message) data = pack(self.PACK_FORMAT, len(data)) + data length = len(data) if not length: return
try: n = os.write(self.out_d, data) except IOError as e: if e.errno == errno.EPIPE: raise EOFError('write error, EPIPE') raise IOError('write error, io error') if n == 0: raise EOFError('write error, no data')
def close(self): os.close(self.in_d) os.close(self.out_d)
class Protocol(object):
def __init__(self): self.port = Port()
def handle(self, message): name = message[0] args = message[1:]
handler = getattr(self, 'handler_{}'.format(name), None) if handler is None: return 'Error', 'Dose not exsit handler'
try: response = handler(*args) except TypeError: response = 'TypeError', 'function_clause' return response
def run(self): while True: try: message = self.port.read() response = self.handle(message) self.port.write(response) except ValueError as e: response = 'ValueError', e.message self.port.write(response) except EOFError as e: response = 'EOFError', e.message self.port.write(response) break
class MyProtocol(Protocol):
def handler_twice(self, x): return 2 * int(x)
def handler_sum(self, x, y): return int(x) + int(y)
def handler_hello(self, name): return 'Hello, {}'.format(name)
if __name__ == '__main__': MyProtocol().run()
|