Skip to content

Commit

Permalink
Added support for trio (#91)
Browse files Browse the repository at this point in the history
Closes #24
  • Loading branch information
etianen committed Feb 8, 2024
1 parent e7d3f70 commit d84ac8e
Show file tree
Hide file tree
Showing 19 changed files with 478 additions and 69 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
- python-version: "3.12"
- lib-versions: "pytest~=7.0"
- lib-versions: "pytest~=8.0"
- lib-versions: "trio~=0.22.0"
- lib-versions: "trio~=0.23.0"
- lib-versions: "trio~=0.24.0"
fail-fast: false
env:
COVERAGE_FILE: .coverage.${{ matrix.python-version }}${{ matrix.lib-versions }}
Expand All @@ -39,7 +42,7 @@ jobs:
- name: Install dependencies
run: poetry install --all-extras
- name: Install lib versions
run: poetry run pip install -e .[pytest] ${{ matrix.lib-versions }}
run: poetry run pip install -e .[pytest,trio] ${{ matrix.lib-versions }}
if: ${{ matrix.lib-versions }}
# Run checks.
- name: Check (ruff)
Expand Down
10 changes: 10 additions & 0 deletions docs/api/logot.trio.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:mod:`logot.trio`
=================

.. automodule:: logot.trio


API reference
-------------

.. autoclass:: TrioWaiter
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"pytest": ("https://docs.pytest.org/en/latest/", None),
"trio": ("https://trio.readthedocs.io/en/latest", None),
}

nitpicky = True
10 changes: 10 additions & 0 deletions docs/include/testing-async-notes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. currentmodule:: logot

.. note::

Use the ``timeout`` argument to :meth:`Logot.await_for` to configure how long to wait before the test fails. This can
be configured globally with the ``timeout`` argument to :class:`Logot`, defaulting to :attr:`Logot.DEFAULT_TIMEOUT`.

.. seealso::

See :doc:`/log-pattern-matching` for examples of how to wait for logs that may arrive in an unpredictable order.
10 changes: 2 additions & 8 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,7 @@ Use :meth:`Logot.await_for` to pause your test until the expected logs arrive or
asyncio.create_task(app.start())
await logot.await_for(logged.info("App started"))
.. note::

Use the ``timeout`` argument to :meth:`Logot.await_for` to configure how long to wait before the test fails. This can
be configured globally with the ``timeout`` argument to :class:`Logot`, defaulting to :attr:`Logot.DEFAULT_TIMEOUT`.

.. seealso::

See :doc:`/log-pattern-matching` for examples of how to wait for logs that may arrive in an unpredictable order.
.. include:: /include/testing-async-notes.rst


Testing synchronous code
Expand Down Expand Up @@ -153,6 +146,7 @@ Learn more about :mod:`logot` with the following guides:
using-pytest
using-unittest
installing
integrations/index
api/index

.. toctree::
Expand Down
14 changes: 10 additions & 4 deletions docs/installing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@ Install :mod:`logot` like any other Python library:
Installing package ``extras``
-----------------------------

:mod:`logot` provides package ``extras`` to ensure compatibility with supported 3rd-party integrations.
:mod:`logot` provides package ``extras`` to ensure compatibility with supported
:doc:`3rd-party integrations </integrations/index>`.

.. code:: bash
pip install 'logot[pytest]'
pip install 'logot[pytest,trio]'
Package extras for supported 3rd-party integrations:
Use the following package ``extras`` for supported 3rd-party integrations:

- ``pytest`` - :doc:`using-pytest`
- ``pytest`` - :doc:`/using-pytest`
- ``trio`` - :doc:`/integrations/trio`

.. seealso::

See :doc:`/integrations/index` usage guide.


Installing with Poetry
Expand Down
53 changes: 53 additions & 0 deletions docs/integrations/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
3rd-party integrations
======================

.. currentmodule:: logot

:mod:`logot` integrates with a number of 3rd-party frameworks to extend its functionality.

.. note::

Adding support for additional 3rd-party frameworks is an excellent way of
`contributing <https://github.com/etianen/logot/blob/main/CONTRIBUTING.md>`_ to :mod:`logot`! 🙇


.. _integrations-async:

Asynchronous frameworks
-----------------------

Integrations with 3rd-party asynchronous frameworks extend :meth:`Logot.await_for`, allowing you to
:ref:`test asynchronous code <index-testing-async>` using your framework of choice. 💪

Learn about supported 3rd-party asynchronous frameworks:

.. toctree::
:maxdepth: 1

trio

.. seealso::

See :ref:`index-testing-async` usage guide.

See :class:`AsyncWaiter` API reference for integrating with 3rd-party asynchronous frameworks.


.. _integrations-logging:

Logging frameworks
------------------

Integrations with 3rd-party logging frameworks extend :meth:`Logot.capturing`, allowing you to
:doc:`capture logs </log-capturing>` using your framework of choice. 💪

Learn about supported 3rd-party logging frameworks:

.. toctree::
:maxdepth: 1

.. seealso::

See :doc:`/log-capturing` usage guide.

See :class:`Capturer` API reference for integrating with 3rd-party logging frameworks.
92 changes: 92 additions & 0 deletions docs/integrations/trio.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Using with :mod:`trio`
======================

.. currentmodule:: logot

Use :meth:`Logot.await_for` to pause your test until the expected logs arrive or the ``timeout`` expires:

.. code:: python
from logot import Logot, logged
async def test_app(logot: Logot) -> None:
async with trio.open_nursery() as nursery:
nursery.start_soon(app.start())
await logot.await_for(logged.info("App started"))
.. include:: /include/testing-async-notes.rst


Installing
----------

Ensure :mod:`logot` is installed alongside a compatible :mod:`trio` version by adding the ``trio`` extra:

.. code:: bash
pip install 'logot[trio]'
.. seealso::

See :ref:`installing-extras` usage guide.


Enabling for :mod:`pytest`
--------------------------

Enable :mod:`trio` support in your :external+pytest:doc:`pytest configuration <reference/customize>`:

.. code:: ini
# pytest.ini or .pytest.ini
[pytest]
logot_async_waiter = logot.trio.TrioWaiter
.. code:: toml
# pyproject.toml
[tool.pytest.ini_options]
logot_async_waiter = "logot.trio.TrioWaiter"
.. seealso::

See :doc:`/using-pytest` usage guide.


Enabling for :mod:`unittest`
----------------------------

Enable :mod:`trio` support in your :class:`logot.unittest.LogotTestCase`:

.. code:: python
from logot.trio import TrioWaiter
class MyAppTest(LogotTestCase):
logot_async_waiter = TrioWaiter
.. seealso::

See :doc:`/using-unittest` usage guide.


Enabling manually
-----------------

Enable :mod:`trio` support for your :class:`Logot` instance:

.. code:: python
from logot.trio import TrioWaiter
logot = Logot(async_waiter=TrioWaiter)
Enable :mod:`trio` support for a single :meth:`Logot.await_for` call:

.. code:: python
await logot.await_for(logged.info("App started"), async_waiter=TrioWaiter)
.. seealso::

See :class:`Logot` and :meth:`Logot.await_for` API reference.
30 changes: 2 additions & 28 deletions docs/log-capturing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ Use a supported test framework integration for automatic log capturing in tests:
- :doc:`/using-unittest`


.. _log-capturing-logging:

Capturing :mod:`logging` logs
-----------------------------
Configuring
-----------

The :meth:`Logot.capturing` method defaults to capturing **all** records from the root logger. Customize this with the
``level`` and ``logger`` arguments to :meth:`Logot.capturing`:
Expand All @@ -41,27 +39,3 @@ careful to avoid capturing duplicate logs with overlapping calls to :meth:`Logot
.. seealso::

See :class:`Logot` and :meth:`Logot.capturing` API reference.


.. _log-capturing-3rd-party:

Capturing 3rd-party logs
------------------------

Any 3rd-party logging library can be integrated with :mod:`logot` by sending :class:`Captured` logs to
:meth:`Logot.capture`:

.. code:: python
def on_foo_log(logot: Logot, record: FooRecord) -> None:
logot.capture(Captured(record.levelname, record.msg))
foo_logger.add_handler(on_foo_log)
.. note::

Using a context manager to set up and tear down log capture for every test run is *highly recommended*!

.. seealso::

See :class:`Captured` and :meth:`Logot.capture` API reference.
8 changes: 2 additions & 6 deletions logot/_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@ class Captured:
A captured log record.
Send :class:`Captured` logs to :meth:`Logot.capture` to integrate with
:ref:`3rd-party logging frameworks <log-capturing-3rd-party>`.
:ref:`3rd-party logging frameworks <integrations-logging>`.
.. note::
This class is for integration with :ref:`3rd-party logging frameworks <log-capturing-3rd-party>`. It is not
This class is for integration with :ref:`3rd-party logging frameworks <integrations-logging>`. It is not
generally used when writing tests.
.. seealso::
See :ref:`log-capturing-3rd-party` usage guide.
:param levelname: See :attr:`Captured.levelname`.
:param msg: See :attr:`Captured.msg`.
:param levelno: See :attr:`Captured.levelno`.
Expand Down
6 changes: 2 additions & 4 deletions logot/_logged.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ class Logged(ABC):
.. important::
:class:`Logged` instances are immutable and can be reused between tests.
.. note::
This is an abstract class and cannot be instantiated. Use the helpers in :mod:`logot.logged` to create log
patterns.
:class:`Logged` instances are immutable and can be reused between tests.
.. seealso::
See :doc:`/log-pattern-matching` usage guide.
Expand Down
16 changes: 6 additions & 10 deletions logot/_logot.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Logot:
.. note::
This is for integration with :ref:`3rd-party logging frameworks <log-capturing-3rd-party>`.
This is for integration with :ref:`3rd-party logging frameworks <integrations-logging>`.
Defaults to :attr:`Logot.DEFAULT_CAPTURER`.
"""
Expand All @@ -81,7 +81,7 @@ class Logot:
.. note::
This is for integration with 3rd-party asynchronous frameworks.
This is for integration with :ref:`3rd-party asynchronous frameworks <integrations-async>`.
Defaults to :attr:`Logot.DEFAULT_ASYNC_WAITER`.
"""
Expand Down Expand Up @@ -121,7 +121,7 @@ def capturing(
:attr:`Logot.DEFAULT_LEVEL`.
:param logger: A logger name to capture logs from. Defaults to :attr:`Logot.DEFAULT_LOGGER`.
:param capturer: Protocol used to capture logs. This is for integration with
:ref:`3rd-party logging frameworks <log-capturing-3rd-party>`. Defaults to :attr:`Logot.capturer`.
:ref:`3rd-party logging frameworks <integrations-logging>`. Defaults to :attr:`Logot.capturer`.
"""
if capturer is None:
capturer = self.capturer
Expand All @@ -139,13 +139,9 @@ def capture(self, captured: Captured) -> None:
.. note::
This method is for integration with :ref:`3rd-party logging frameworks <log-capturing-3rd-party>`. It is not
This method is for integration with :ref:`3rd-party logging frameworks <integrations-logging>`. It is not
generally used when writing tests.
.. seealso::
See :ref:`log-capturing-3rd-party` usage guide.
:param captured: The captured log.
"""
with self._lock:
Expand Down Expand Up @@ -215,7 +211,7 @@ async def await_for(
:param logged: The expected :doc:`log pattern </log-pattern-matching>`.
:param timeout: How long to wait (in seconds) before failing the test. Defaults to :attr:`Logot.timeout`.
:param async_waiter: Protocol used to pause tests until expected logs arrive. This is for integration with
3rd-party asynchronous frameworks. Defaults to :attr:`Logot.async_waiter`.
:ref:`3rd-party asynchronous frameworks <integrations-async>`. Defaults to :attr:`Logot.async_waiter`.
:raises AssertionError: If the expected ``log`` pattern does not arrive within ``timeout`` seconds.
"""
if async_waiter is None:
Expand Down Expand Up @@ -286,7 +282,7 @@ class Capturer(ABC):
.. note::
This class is for integration with :ref:`3rd-party logging frameworks <log-capturing-3rd-party>`. It is not
This class is for integration with :ref:`3rd-party logging frameworks <integrations-logging>`. It is not
generally used when writing tests.
"""

Expand Down
Loading

0 comments on commit d84ac8e

Please sign in to comment.