__init__.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. r"""
  2. Wslink allows easy, bi-directional communication between a python server and a
  3. javascript client over a websocket.
  4. wslink.server creates the python server
  5. wslink.websocket handles the communication
  6. """
  7. import asyncio
  8. import functools
  9. from .uri import checkURI
  10. __license__ = "BSD-3-Clause"
  11. def register(uri):
  12. """
  13. Decorator for RPC procedure endpoints.
  14. """
  15. def decorate(f):
  16. # called once when method is decorated, because we return 'f'.
  17. assert callable(f)
  18. if not hasattr(f, "_wslinkuris"):
  19. f._wslinkuris = []
  20. f._wslinkuris.append({"uri": checkURI(uri)})
  21. return f
  22. return decorate
  23. #############################################################################
  24. #
  25. # scheduling methods
  26. #
  27. # Allow scheduling both callbacks and coroutines from both sync and async
  28. # methods.
  29. #
  30. #############################################################################
  31. def schedule_callback(delay, callback, *args, **kwargs):
  32. """
  33. Schedule callback (which is passed args and kwargs) to be called on
  34. running event loop after delay seconds (can be floating point). Returns
  35. asyncio.TimerHandle on which cancel() can be called to cancel the
  36. eventual invocation of the callback.
  37. """
  38. # Using "asyncio.get_running_loop()" requires the event loop to be running
  39. # already, so we use "asyncio.get_event_loop()" here so that we can support
  40. # scheduling tasks before the server is started.
  41. loop = asyncio.get_event_loop()
  42. return loop.call_later(delay, functools.partial(callback, *args, **kwargs))
  43. def schedule_coroutine(delay, coro_func, *args, **kwargs):
  44. """
  45. Creates a coroutine out of the provided coroutine function coro_func and
  46. the provided args and kwargs, then schedules the coroutine to be called
  47. on the running event loop after delay seconds (delay can be float or int).
  48. Returns asyncio.Task on which cancel() can be called to cancel the running
  49. of the coroutine.
  50. The coro_func parameter should not be a coroutine, but rather a coroutine
  51. function (a function defined with async). The reason for this is we want
  52. to defer creation of the actual coroutine until we're ready to schedule it
  53. with "ensure_future". Otherwise every time we cancel a TimerTask
  54. returned by "call_later", python prints "RuntimeWarning: coroutine
  55. '<coro-name>' was never awaited".
  56. """
  57. # See method above for comment on "get_event_loop()" vs "get_running_loop()".
  58. loop = asyncio.get_event_loop()
  59. coro_partial = functools.partial(coro_func, *args, **kwargs)
  60. return loop.call_later(delay, lambda: asyncio.ensure_future(coro_partial()))