PageRenderTime 37ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/synapse/message.py

https://github.com/mardiros/python-synapse
Python | 264 lines | 192 code | 61 blank | 11 comment | 7 complexity | b2f5a4c83b796107de0ee6cc3ee77a85 MD5 | raw file
  1. #!/usr/bin/env python
  2. # -*- coding: utf8 -*-
  3. import random
  4. try:
  5. import simplejson as json
  6. except ImportError:
  7. import json
  8. class MessageException(Exception):
  9. pass
  10. class MessageInvalidException(MessageException):
  11. def __init__(self, msg):
  12. self._msg = msg
  13. def __str__(self):
  14. return 'message invalid : "%s"' % str(self._msg)
  15. class CodecException(Exception):
  16. pass
  17. class CodecInvalidException(CodecException):
  18. def __init__(self, key):
  19. self._key = key
  20. def __str__(self):
  21. return 'codec %s is not supported' % self._key
  22. class Message(object):
  23. def __init__(self, id=None):
  24. self._id = id
  25. @property
  26. def id(self):
  27. if not self._id:
  28. rand_min = 0
  29. rand_max = 2 ** 32
  30. self._id = random.randint(rand_min, rand_max)
  31. return self._id
  32. class ReplyMessage(Message):
  33. type = 'reply'
  34. def __init__(self, src, data, id):
  35. Message.__init__(self, id)
  36. self.src = src
  37. self.data = data
  38. @property
  39. def attrs(self):
  40. return {'src': self.src,
  41. 'data': self.data}
  42. class HelloMessage(Message):
  43. type = 'hello'
  44. def __init__(self, src, uri, id=None):
  45. Message.__init__(self, id)
  46. self.src = src
  47. self.uri = uri
  48. @property
  49. def attrs(self):
  50. return {'src': self.src,
  51. 'uri': self.uri}
  52. class ByeMessage(Message):
  53. type = 'bye'
  54. def __init__(self, src, id=None):
  55. Message.__init__(self, id)
  56. self.src = src
  57. @property
  58. def attrs(self):
  59. return {'src': self.src}
  60. class WhereIsMessage(Message):
  61. type = 'where_is'
  62. def __init__(self, name, id=None):
  63. Message.__init__(self, id)
  64. self.name = name
  65. @property
  66. def attrs(self):
  67. return {'name': self.name}
  68. class IsAtMessage(Message):
  69. type = 'is_at'
  70. def __init__(self, name, uri, id=None):
  71. Message.__init__(self, id)
  72. self.name = name
  73. self.uri = uri
  74. @property
  75. def attrs(self):
  76. return {'name': self.name,
  77. 'uri': self.uri}
  78. class UnknownNodeMessage(Message):
  79. type = 'unknown_node'
  80. def __init__(self, name, id=None):
  81. super(UnknownNodeMessage, self).__init__(id)
  82. self.name = name
  83. @property
  84. def attrs(self):
  85. return {'name': self.name}
  86. class AckMessage(Message):
  87. type = 'ack'
  88. def __init__(self, src, id=None):
  89. Message.__init__(self, id)
  90. self.src = src
  91. @property
  92. def attrs(self):
  93. return {'src': self.src}
  94. class NackMessage(Message):
  95. type = 'nack'
  96. def __init__(self, src, msg, id=None):
  97. Message.__init__(self, id)
  98. self.src = src
  99. self.msg = msg
  100. @property
  101. def attrs(self):
  102. return {'src': self.src,
  103. 'msg': self.msg}
  104. class IsAlive(Message):
  105. type = 'is_alive'
  106. @property
  107. def attrs(self):
  108. return {}
  109. class Alive(Message):
  110. type = 'alive'
  111. def __init__(self, pid, id=None):
  112. Message.__init__(self, id)
  113. self.pid = pid
  114. @property
  115. def attrs(self):
  116. return {'pid': self.pid}
  117. class MessageCodec(object):
  118. def loads(self, msgstring):
  119. raise NotImplementedError()
  120. def dumps(self, msg):
  121. raise NotImplementedError()
  122. class DateTimeJSONEncoder(json.JSONEncoder):
  123. """
  124. JSONEncoder subclass that encodes objects:
  125. - datetime.datetime
  126. - datetime.date
  127. - datetime.time
  128. - xmlrpclib.DateTime
  129. """
  130. def default(self, obj):
  131. import datetime
  132. import xmlrpclib
  133. if isinstance(obj, datetime.datetime) or \
  134. isinstance(obj, datetime.date) or \
  135. isinstance(obj, datetime.time):
  136. return obj.isoformat()
  137. elif isinstance(obj, xmlrpclib.DateTime):
  138. data = obj.value.replace('-', '').replace(' ', 'T')
  139. if '.' not in data:
  140. data += '.000'
  141. return datetime.datetime.strptime(data,
  142. "%Y%m%dT%H:%M:%S.%f").isoformat()
  143. else:
  144. return super(self.__class__, self).default(obj)
  145. class MessageCodecJSONRPC(MessageCodec):
  146. def __init__(self, config):
  147. pass
  148. def loads(self, msgstring):
  149. try:
  150. jsonrpc_msg = json.loads(msgstring)
  151. msgtype = jsonrpc_msg['method']
  152. msgattrs = jsonrpc_msg['params']
  153. msg_dict = {'type': msgtype}
  154. msg_dict.update(msgattrs)
  155. msg_dict['id'] = jsonrpc_msg['id']
  156. msg = makeMessage(msg_dict)
  157. return msg
  158. except Exception, err:
  159. raise CodecException(str(err))
  160. def dumps(self, msg):
  161. try:
  162. jsonrpc_msg = {
  163. 'method': msg.type,
  164. 'params': msg.attrs,
  165. 'id': msg.id}
  166. return json.dumps(jsonrpc_msg, cls=DateTimeJSONEncoder)
  167. except Exception, err:
  168. raise CodecException(str(err))
  169. def makeCodec(config):
  170. dispatch = {'jsonrpc': MessageCodecJSONRPC}
  171. try:
  172. codec_instance = dispatch[config['type']](config)
  173. except KeyError, err:
  174. raise CodecInvalidException(err)
  175. return codec_instance
  176. def makeMessage(msg):
  177. """instanciate a message from a mapping
  178. :Exceptions:
  179. - `MessageInvalidException`
  180. """
  181. msgtmp = msg.copy()
  182. subclasses = Message.__subclasses__()
  183. dispatch = dict((cls.type, cls) for cls in subclasses)
  184. msgtype = msgtmp['type']
  185. del msgtmp['type']
  186. try:
  187. message_instance = dispatch[msgtype](**msgtmp)
  188. except KeyError:
  189. raise MessageInvalidException("%s is not a subclass of Message" %
  190. msgtype)
  191. return message_instance