.. _changelog:

================
 Change history
================

.. _version-1.11.0:

1.11.0
======
:release-date: 2018-04-17 1:23 P.M PST
:release-by: Ask Solem

- Supervisor: Fixes bug with max restart triggering too early.

- Supervisor: Also restart child services.

- Service: Now supports ``__post_init__`` like Python 3.7 dataclasses.

- Service: Crash is logged even if crashed multiple times.

.. _version-1.10.4:

1.10.4
======
:release-date: 2018-04-13 3:53 P.M PST
:release-by: Ask Solem

- Supervisor: Log full traceback when restarting service.

.. _version-1.10.3:

1.10.3
======
:release-date: 2018-04-11 10:58 P.M PST
:release-by: Ask Solem

- setup_logging: now ensure logging is setup by clearing root logger handlers.

.. _version-1.10.2:

1.10.2
======
:release-date: 2018-04-03 4:50 P.M PST
:release-by: Ask Solem

- Fixed wrong version number in Changelog.

.. _version-1.10.1:

1.10.1
=======
:release-date: 2018-04-03 4:43 P.M PST
:release-by: Ask Solem

- Service.wait: If the future we are waiting for is cancelled we must
                propagate :exc:`CancelledError`.

.. _version-1.10.0:

1.10.0
======
:release-date: 2018-03-30 12:36 P.M PST
:release-by: Ask Solem

- New supervisor: :class:`~mode.supervisors.ForfeitOneForOneSupervisor`.

    If a service in the group crashes we give up on that service
    and don't start it again.

- New supervisor: :class:`~mode.supervisor.ForfeitOneForAllSupervisor`.

    If a service in the group crashes we give up on it, but also
    stop all services in the group and give up on them also.

- Service Logging: Renamed ``self.log.crit`` to ``self.log.critical``.

    The old name is still available and is not deprecated at this time.


.. _version-1.9.2:

1.9.2
=====
:release-date: 2018-03-20 10:17 P.M PST
:release-by: Ask Solem

- Adds ``FlowControlEvent.clear()`` to clear all contents of flow
  controlled queues.

- :class:`~mode.utils.futures.FlowControlEvent` now starts in a suspended
  state.

    To disable this pass ``FlowControlEvent(initially_suspended=False))``.

- Adds ``Service.service_reset`` method to reset service
  start/stopped/crashed/etc., flags

.. _version-1.9.1:

1.9.1
=====
:release-date: 2018-03-05 11:51 P.M PST
:release-by: Ask Solem

- No longer depends on :pypi:`terminaltables`.

.. _version-1.9.0:

1.9.0
=====
:release-date: 2018-03-05 11:33 P.M PST
:release-by: Ask Solem

Backward Incompatible Changes
=============================

- Module ``mode.utils.debug`` renamed to :mod:`mode.debug`.

    This is unlikely to affect users as this module is only used by mode
    internally.

    This module had to move because it imports ``mode.Service``, and
    the :mod:`mode.utils` package is not allowed to import from the
    :mod:`mode` package at all.

News
====

- Added function :func:`mode.utils.import.smart_import`.

- Added non-async version of :class:`mode.Signal`: :class:`mode.SyncSignal`.

    The signal works exactly the same as the asynchronous version, except
    ``Signal.send`` must not be :keyword:`await`-ed::

        on_configured = SyncSignal()
        on_configured.send(sender=obj)


- Added method ``iterate`` to :class:`mode.utils.imports.FactoryMapping`.

    This enables you to iterate over the extensions added to a
    :mod:`setuptools` entrypoint.

Fixes
=====

- ``StampedeWrapper`` now correctly clears flag when original call done.

.. _version-1.8.0:

1.8.0
=====
:release-date: 2018-02-20 04:01 P.M PST
:release-by: Ask Solem

Backward Incompatible Changes
-----------------------------

- API Change to fix memory leak in ``Service.wait``.

    The ``Service.wait(*futures)`` method was added to be able to wait for
    this list of futures but also stop waiting if the service is stopped or
    crashed::

        import asyncio
        from mode import Service

        class X(Service):
            on_thing_ready: asyncio.Event

            def __post_init__(self):
                self.on_thing_ready = asyncio.Event(loop=loop)

            @Service.task
            async def _my_background_task(self):
                while not self.should_stop:
                    # wait for flag to be set (or service stopped/crashed)
                    await self.wait(self.on_thing_ready.wait())
                    print('FLAG SET')

    The problem with this was

    1) The wait flag would return None and not raise an exception if the
       service is stopped/crashed.
    2) Futures would be scheduled on the event loop but not properly cleaned
       up, creating a very slow memory leak.
    3) No return value was returned for succesful feature.

    So to properly implement this we had to change the API of the ``wait``
    method to return a tuple instead, and to only allow a single coroutine to
    be passed to wait:

        @Service.task
        async def _my_background_task(self):
            while not self.should_stop:
                # wait for flag to be set (or service stopped/crashed)
                result, stopped = await self.wait(self.on_thing_ready)
                if not stopped:
                    print('FLAG SET')

    This way the user can provide an alternate path when the service is
    stopped/crashed while waiting for this event.

    A new shortcut method ``wait_for_stopped(fut)`` was also added::

        # wait for flag to be set (or service stopped/crashed)
        if not await self.wait_for_stopped(self.on_thing_ready):
            print('FLAG SET')

    Moreover, you can now pass :class:`asyncio.Event` objects directly to
    ``wait()``.

News
----

- Added :class:`mode.utils.collections.DictAttribute`.

- Added :class:`mode.utils.collections.AttributeDict`.

Bugs
----

- Signals can create clone of signal with default sender already set

    .. code-block:: python

        signal: Signal[int] = Signal()
        signal = signal.with_default_sender(obj)

.. _version-1.7.0:

1.7.0
=====
:release-date: 2018-02-05 12:28 P.M PST
:release-by: Ask Solem

- Adds :mod:`mode.utils.aiter` for missing ``aiter`` and ``anext`` functions.

- Adds :mod:`mode.utils.futures` for :class:`asyncio.Task` related tools.

- Adds :mod:`mode.utils.collections` for custom mapping/set and list
  data structures.

- Adds :mod:`mode.utils.imports` for importing modules at runtime,
  as well as utilities for typed :mod:`setuptools` entry-points.

- Adds :mod:`mode.utils.text` for fuzzy matching user input.

.. _version-1.6.0:

1.6.0
=====
:release-date: 2018-02-05 11:10 P.M PST
:release-by: Ask Solem

- Fixed bug where ``@Service.task`` background tasks were not started
  in subclasses.

- Service: Now has two exit stacks: ``.exit_stack`` & ``.async_exit_stack``.

    This is a backward incompatible change, but probably nobody was accessing
    ``.exit_stack`` directly.

    Use ``await Service.enter_context(ctx)`` with both regular and
    asynchronous context managers::

        class X(Service):

            async def on_start(self) -> None:
                # works with both context manager types.
                await self.enter_context(async_context)
                await self.enter_context(context)

- Adds :func:`~mode.utils.contextlib.asynccontextmanager`` decorator
  from CPython 3.7b1.

    This decorator works exactly the same as
    :func:`contextlib.contextmanager`, but for :keyword:`async with`.

    Import it from :mod:`mode.utils.contexts`::

        from mode.utils.contexts import asynccontextmanager

        @asynccontextmanager
        async def connection_or_default(conn: Connection = None) -> Connection:
            if connection is None:
                async with connection_pool.acquire():
                    yield
            else:
                yield connection

        async def main():
            async with connection_or_default() as connection:
                ...

- Adds :class:`~mode.utils.contexts.AsyncExitStack` from CPython 3.7b1

    This works like :class:`contextlib.ExitStack`, but for asynchronous
    context managers used with :keyword:`async with`.

- Logging: Worker debug log messages are now colored blue when colors are
  enabled.


.. _version-1.5.0:

1.5.0
=====
:release-date: 2018-01-04 03:43 P.M PST
:release-by: Ask Solem

- Service: Adds new ``await self.add_context(context)``

    This adds a new context manager to be entered when the service starts,
    and exited once the service exits.

    The context manager can be either a :class:`typing.AsyncContextManager`
    (:keyword:`async with`) or a
    regular :class:`typing.ContextManager` (:keyword:`with`).

- Service: Added ``await self.add_runtime_dependency()`` which unlike
  ``add_dependency`` starts the dependent service if the self is already
  started.

- Worker: Now supports a new ``console_port`` argument to specify a port
  for the :pypi:`aiomonitor` console, different than the default (50101).

    .. note::

        The aiomonitor console is only started when ``Worker(debug=True, ...)``
        is set.

.. _version-1.4.0:

1.4.0
=====
:release-date: 2017-12-21 09:50 A.M PST
:release-by: Ask Solem

- Worker: Add support for parameterized logging handlers.

    Contributed by Prithvi Narasimhan.

.. _version-1.3.0:

1.3.0
=====
:release-date: 2017-12-04 01:17 P.M PST
:release-by: Ask Solem

- Now supports color output in logs when logging to a terminal.

- Now depends on :pypi:`colorlog`

- Added :class:`mode.Signal`: async. implementation of the observer
  pattern (think Django signals).

- DependencyGraph is now a generic type: ``DependencyGraph[int]``

- Node is now a generic type: ``Node[Service]``.

.. _version-1.2.1:

1.2.1
=====
:release-date: 2017-11-06 04:50 P.M PST
:release-by: Ask Solem

- Service: Subclasses can now override a Service.task method.

    Previously it would unexpectedly start two tasks:
    the task defined in the superclass and the task defined in
    the subclass.

.. _version-1.2.0:

1.2.0
=====
:release-date: 2017-11-02 03:17 P.M PDT
:release-by: Ask Solem

- Renames PoisonpillSupervisor to CrashingSupervisor.

- Child services now stopped even if not fully started.

    Previously ``child_service.stop()`` would not be called
    if `child_service.start()` never completed, but as a service
    might be in the process of starting other child services, we need
    to call stop even if not fully started.

.. _version-1.1.1:

1.1.1
=====
:release-date: 2017-10-25 04:34 P.M PDT
:release-by: Ask Solem

- Added alternative event loop implementations: eventlet, gevent, uvloop.

    E.g. to use gevent as the event loop, install mode using::

        $ pip install mode[gevent]

    and add this line to the top of your worker entrypoint module::

        import mode.loop
        mode.loop.use('gevent')

- Service: More fixes for the weird `__init_subclass__` behavior
  only seen in Python 3.6.3.

- ServiceThread: Now propagates errors raised in the thread
  to the main thread.

.. _version-1.1.0:

1.1.0
=====
:release-date: 2017-10-19 01:35 P.M PDT
:release-by: Ask Solem

- ServiceThread: Now inherits from Service, and uses
  ``loop.run_in_executor()`` to start the service as a thread.

- setup_logging: filename argument is now respected.

.. _version-1.0.2:

1.0.2
=====
:release-date: 2017-10-10 01:51 P.M PDT
:release-by: Ask Solem

- Adds support for Python 3.6.0

- Adds backports of typing improvements in CPython 3.6.1
  to ``mode.utils.compat``: ``AsyncContextManager``, ``ChainMap``,
  ``Counter``, and ``Deque``.

- ``Supervisor.add`` and ``.discard`` now takes an arbitrary number
  of services to add/discard as star arguments.

- Fixed typo in example: ``Service.task`` -> ``mode.Service.task``.

    Contributed by Xu Jing.

.. _version-1.0.1:

1.0.1
=====
:release-date: 2017-10-05 02:53 P.M PDT
:release-by: Ask Solem

- Fixes compatibility with Python 3.6.3.

    Python 3.6.3 badly broke ``__init_subclass__``, in such a way that
    any class attribute set is set for all subclasses.

.. _version-1.0.0:

1.0.0
=====
:release-date: 2017-10-04 01:29 P.M PDT
:release-by: Ask Solem

- Initial release
