framework.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import tornado.websocket
  2. import time, json
  3. from collections import defaultdict
  4. from tornado.gen import coroutine, sleep
  5. import random, string
  6. from contextlib import contextmanager
  7. from tornado.log import gen_log
  8. class Future:
  9. def __iter__(self):
  10. result = yield
  11. return result
  12. __await__ = __iter__ # make compatible with 'await' expression
  13. class Task:
  14. @contextmanager
  15. def ws_context(self):
  16. """
  17. >>> with ws_context():
  18. ... res = self.coros[-1].send(data)
  19. """
  20. Global.active_ws = self.ws
  21. Global.active_coro_id = self.coro_id
  22. try:
  23. yield
  24. finally:
  25. Global.active_ws = None
  26. Global.active_coro_id = None
  27. @staticmethod
  28. def gen_coro_id(coro=None):
  29. name = 'coro'
  30. if hasattr(coro, '__name__'):
  31. name = coro.__name__
  32. random_str = ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(10))
  33. return '%s-%s' % (name, random_str)
  34. def __init__(self, coro, ws):
  35. print('into Task __init__ `', coro, ws)
  36. self.ws = ws
  37. self.coro = coro
  38. self.coro_id = None
  39. self.result = None
  40. self.task_finished = False # 协程完毕
  41. self.coro_id = self.gen_coro_id(self.coro)
  42. @coroutine
  43. def step(self, result=None):
  44. try:
  45. with self.ws_context():
  46. res = self.coro.send(result)
  47. while res is not None:
  48. r = yield res
  49. with self.ws_context():
  50. res = self.coro.send(r)
  51. except StopIteration as e:
  52. if len(e.args) == 1:
  53. self.result = e.args[0]
  54. self.task_finished = True
  55. gen_log.debug('Task[%s] finished, self.coros:%s', self.coro_id, self.ws.coros)
  56. # raise
  57. class Msg:
  58. mid2callback = defaultdict(list)
  59. @staticmethod
  60. def gen_msg_id():
  61. mid = '%s-%s' % (Global.active_ws.sid, int(time.time()))
  62. return mid
  63. @classmethod
  64. def add_callback(cls, msg_id, callback):
  65. cls.mid2callback[msg_id].append(callback)
  66. @classmethod
  67. def get_callbacks(cls, msg_id):
  68. return cls.mid2callback[msg_id]
  69. @classmethod
  70. def get_callbacks(cls, msg_id):
  71. return cls.mid2callback[msg_id]
  72. @classmethod
  73. def unregister_msg(cls, msg_id):
  74. del cls.mid2callback[msg_id]
  75. class Global:
  76. # todo issue: with 语句可能发生嵌套,导致内层with退出时,将属性置空
  77. active_ws: "EchoWebSocket" = None
  78. active_coro_id = None