From 11795b601e7a79fdbc76b831cf54b5521ab773f0 Mon Sep 17 00:00:00 2001 From: Sebastien Jourdain Date: Mon, 9 Sep 2024 11:33:51 -0600 Subject: [PATCH] fix(tools.serve): resolve coroutine --- trame/tools/serve.py | 2 +- trame/utils/__init__.py | 0 trame/utils/exec.py | 62 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 trame/utils/__init__.py create mode 100644 trame/utils/exec.py diff --git a/trame/tools/serve.py b/trame/tools/serve.py index 3be1b24d..56d92681 100644 --- a/trame/tools/serve.py +++ b/trame/tools/serve.py @@ -51,7 +51,7 @@ async def on_msg_from_server(binary, content): try: await ws_network.prepare(request) ws_app = app.server._server.ws - connection = ws_app.connect() + connection = await ws_app.connect() connection.on_message(on_msg_from_server) async for msg in ws_network: await connection.send(msg.type == aiohttp.WSMsgType.BINARY, msg) diff --git a/trame/utils/__init__.py b/trame/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/trame/utils/exec.py b/trame/utils/exec.py new file mode 100644 index 00000000..58c814b9 --- /dev/null +++ b/trame/utils/exec.py @@ -0,0 +1,62 @@ +import asyncio + + +class Throttle: + """ + Helper class that wrap a function with a given max execution rate. + By default the rate is set to execute no more than once a second. + + :param fn: the function to call. + :type fn: function + + :param ts: Number of seconds to wait before the next execution. + :type ts: float + """ + + def __init__(self, fn, ts=1): + self._ts = ts + self._fn = fn + self._requests = 0 + self._pending = False + self._last_args = [] + self._last_kwargs = {} + + @property + def rate(self): + """Number of maximum executions per second""" + return 1.0 / self._ts + + @rate.setter + def rate(self, rate): + """Update the maximum number of executions per seconds""" + self._ts = 1.0 / rate + + @property + def delta_t(self): + """Number of seconds to wait between execution""" + return self._ts + + @delta_t.setter + def delta_t(self, seconds): + """Update the number of seconds to wait between execution""" + self._ts = seconds + + async def _trottle(self): + self._pending = True + if self._requests: + self._fn(*self._last_args, **self._last_kwargs) + self._requests = 0 + + await asyncio.sleep(self._ts) + if self._requests > 0: + await self._trottle() + self._pending = False + + def __call__(self, *args, **kwargs): + """Function call wrapper that will throttle the actual function provided at construction""" + self._requests += 1 + self._last_args = args + self._last_kwargs = kwargs + + if not self._pending: + asyncio.create_task(self._trottle())