=====
:release-date: 2018-11-30 4:48 P.M PDT
:release-by: Ask Solem (:github_user:`ask`)
- ``ServiceThread`` no longer uses ``run_in_executor``.
Since services are long running, it is not a good idea for
them to block pool worker threads. Instead we run one
thread for every ServiceThread.
- Adds :class:`~mode.threads.QueuedServiceThread`
This subclass of :class:`~mode.threads.ServiceThread` enables
the use of a queue to send work to the service thread.
This is useful for services that wrap blocking network clients
for example.
If you have a blocking Redis client you could run it in a separate
thread like this:
.. code-block:: python
class Redis(QueuedServiceThread):
_client: StrictRedis = None
async def on_start(self) -> None:
self._client = StrictRedis()
async def get(self, key):
return await self.call_thread(self._client.get, key)
async def set(self, key, value):
await self.call_thread(self._client.set, key, value)
The actual redis client will be running in a separate thread (with a
separate event loop). The ``get`` and ``set`` methods will delegate
to the thread, and return only when the thread is finished handling
them and is ready with a result:
.. sourcecode:: python
async def use_redis():
We use async-with-statement here, but
can also do `await redis.start()` then `await redis.stop()`
async with Redis() as redis:
await redis.set(key='foo', value='bar')
assert await redis.get(key='foo') == 'bar'
- Collections: ``FastUserSet`` and ``ManagedUserSet`` now implements
all :class:`set` operations.
- Collections are now generic types.
You can now subclass collections with typing information:
- ``class X(FastUserDict[str, int]): ...``
- ``class X(ManagedUserDict[str, int]): ...``
- ``class X(FastUserSet[str]): ...``
- ``class X(ManagedUserSet[str]): ...``
- :func:`~mode.utils.futures.maybe_async` utility now
also works with ``asyncio.coroutine`` decorated coroutines.
- Worker: SIGUSR1 cry handler: Fixed crash when coroutine does not have
``__name__`` attribute.
.. _version-2.0.4: