# AMOS message reader/writer import re class Error(Exception): pass class Message: """ AMOS Message object Fields: type - message type code fields - dictionary of fields sub_messages - list of sub-messages repr(message) converts the message back to AMOS format """ def __init__(self, type): self.type = type self.fields = { } self.sub_messages = [ ] def __repr__(self): result = '{' + self.type + '\n' for key in self.fields: result += key+':' if '\n' in self.fields[key]: result += '\n'+self.fields[key] if not self.fields[key].endswith('\n'): result += '\n' result += '.\n' else: result += self.fields[key]+'\n' result += ''.join([ repr(sub_message) for sub_message in self.sub_messages ]) + \ '}\n' return result def get_multiline(self, name): """ Strip newline characters from a multi-line field. """ return ''.join(self.fields[name].split('\n')) def set_multiline(self, name, data): """ Set a multi-line field, inserting newlines every 60 characters. """ self.fields[name] = '\n'.join([ data[i:i+60] for i in xrange(0,len(data),60) ]) + '\n' _START = re.compile(r'^{([A-Z][A-Z][A-Z])\n$') _MULTILINE_FIELD = re.compile(r'^([a-z][a-z][a-z]):\n$') _FIELD = re.compile(r'^([a-z][a-z][a-z]):(.*)\n$') def read_record(file, first_line=None): """ Read a record from a file of AMOS messages On success returns a Message object On end of file raises EOFError On syntax error raises amos.Error """ if first_line is None: first_line = file.readline() if not first_line: raise EOFError() match = _START.match(first_line) if not match: raise Error('Bad start of message', first_line) message = Message(match.group(1)) while True: line = file.readline() match = _MULTILINE_FIELD.match(line) if match: name = match.group(1) message.fields[name] = '' while True: line = file.readline() if line == '.\n': break message.fields[name] += line continue match = _FIELD.match(line) if match: message.fields[match.group(1)] = match.group(2) continue if line == '}\n': break if line.startswith('{'): message.sub_messages.append( read_record(file, line) ) continue raise Error('Bad line',line) return message def iter_records(file): """ Iterate over all the records in a file """ while True: try: yield read_record(file) except EOFError: break if __name__ == '__main__': #Example: pass a message stream from stdin to stdout # print count of each message type to stderr import sys counts = { } for record in iter_records(sys.stdin): counts[record.type] = counts.get(record.type,0) + 1 sys.stdout.write(repr(record)) types = counts.keys() types.sort() for type in types: print >> sys.stderr, '%s x %d' % (type, counts[type])