From 01b64e5588dced81bbd922471b55941e16fd2d26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 23:41:38 +0000 Subject: [PATCH 1/2] build(deps-dev): Bump the minor group across 1 directory with 6 updates Bumps the minor group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [griffe](https://github.com/mkdocstrings/griffe) | `2.0.2` | `2.1.0` | | [mypy](https://github.com/python/mypy) | `2.1.0` | `2.3.0` | | [nox](https://github.com/wntrblm/nox) | `2026.4.10` | `2026.7.11` | | [hypothesis](https://github.com/HypothesisWorks/hypothesis) | `6.155.7` | `6.163.0` | | [pytest](https://github.com/pytest-dev/pytest) | `9.0.3` | `9.1.1` | | [setuptools-scm](https://github.com/pypa/setuptools-scm) | `10.0.5` | `10.2.1` | Updates `griffe` from 2.0.2 to 2.1.0 - [Release notes](https://github.com/mkdocstrings/griffe/releases) - [Changelog](https://github.com/mkdocstrings/griffe/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/griffe/compare/2.0.2...2.1.0) Updates `mypy` from 2.1.0 to 2.3.0 - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v2.1.0...v2.3.0) Updates `nox` from 2026.4.10 to 2026.7.11 - [Release notes](https://github.com/wntrblm/nox/releases) - [Changelog](https://github.com/wntrblm/nox/blob/main/CHANGELOG.md) - [Commits](https://github.com/wntrblm/nox/compare/2026.04.10...2026.07.11) Updates `hypothesis` from 6.155.7 to 6.163.0 - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/v6.155.7...v6.163.0) Updates `pytest` from 9.0.3 to 9.1.1 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.1) Updates `setuptools-scm` from 10.0.5 to 10.2.1 - [Release notes](https://github.com/pypa/setuptools-scm/releases) - [Changelog](https://github.com/pypa/setuptools-scm/blob/main/RELEASE_SYSTEM.md) - [Commits](https://github.com/pypa/setuptools-scm/compare/setuptools-scm-v10.0.5...setuptools-scm-v10.2.1) --- updated-dependencies: - dependency-name: griffe dependency-version: 2.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor - dependency-name: hypothesis dependency-version: 6.163.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor - dependency-name: mypy dependency-version: 2.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor - dependency-name: nox dependency-version: 2026.7.11 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor - dependency-name: pytest dependency-version: 9.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor - dependency-name: setuptools-scm dependency-version: 10.2.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor ... Signed-off-by: dependabot[bot] --- pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ebfd2099..75f25280 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [build-system] requires = [ "setuptools == 82.0.1", - "setuptools_scm[toml] == 10.0.5", + "setuptools_scm[toml] == 10.2.1", "frequenz-repo-config[lib] == 0.18.0", ] build-backend = "setuptools.build_meta" @@ -60,15 +60,15 @@ dev-mkdocs = [ "mkdocstrings[python] == 1.0.6", "mkdocstrings-python == 2.0.5", "pymdownx-superfence-filter-lines == 0.1.0", - "griffe==2.0.2", + "griffe==2.1.0", ] dev-mypy = [ # For checking the noxfile, docs/ script, and tests "frequenz-channels[dev-mkdocs,dev-noxfile,dev-pytest]", - "mypy == 2.1.0", + "mypy == 2.3.0", "types-Markdown == 3.10.2.20260518", ] -dev-noxfile = ["nox == 2026.4.10", "frequenz-repo-config[lib] == 0.18.0"] +dev-noxfile = ["nox == 2026.7.11", "frequenz-repo-config[lib] == 0.18.0"] dev-pylint = [ # For checking the noxfile, docs/ script, and tests "frequenz-channels[dev-mkdocs,dev-noxfile,dev-pytest]", @@ -77,8 +77,8 @@ dev-pylint = [ dev-pytest = [ "async-solipsism == 0.9", "frequenz-repo-config[extra-lint-examples] == 0.18.0", - "hypothesis == 6.155.7", - "pytest == 9.0.3", + "hypothesis == 6.163.0", + "pytest == 9.1.1", "pytest-asyncio == 1.4.0", "pytest-mock == 3.15.1", ] From 7215838d426b3140ef46b539df3b123e6d628309 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 12 Aug 2026 12:25:20 +0000 Subject: [PATCH 2/2] test: Pass an explicit tolerance when comparing timedeltas pytest 9.1.0 added proper support for `datetime`/`timedelta` in `pytest.approx()` (pytest-dev/pytest#8395). As part of it, `approx()` now refuses to guess a tolerance for these types and raises instead: TypeError: pytest.approx() requires an explicit tolerance for datetime/timedelta comparisons: e.g. approx(expected, abs=timedelta(seconds=1)) or approx(expected, rel=0.01) This broke the 12 timer tests comparing the drift against `pytest.approx(timedelta(...))`. Those assertions never really used a tolerance anyway: the drift is exact, as the tests run on the deterministic async-solipsism event loop, so they always succeeded through the exact-equality short-circuit in `approx()`, before any tolerance was computed. They could then simply be turned into plain `==` comparisons, but keeping them approximate stays closer to the original intent and is more robust against future rounding changes, so a small `_approx_timedelta()` helper is added instead. It uses a tolerance of one microsecond, which is the resolution timers work with. A helper is used rather than passing `abs=` at each call site to keep the rationale documented in one place and to avoid reformatting the longest assertions. Signed-off-by: Leandro Lucarella --- tests/test_timer.py | 90 +++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/tests/test_timer.py b/tests/test_timer.py index 3b052aa1..eaafaaad 100644 --- a/tests/test_timer.py +++ b/tests/test_timer.py @@ -7,6 +7,7 @@ import enum import re from datetime import timedelta +from typing import Any import async_solipsism import hypothesis @@ -22,6 +23,21 @@ ) +def _approx_timedelta(expected: timedelta) -> Any: + """Build an approximate comparison for a timedelta. + + `pytest.approx()` requires an explicit tolerance to compare timedeltas, and since + timers work with a microsecond resolution, one microsecond is used as the tolerance. + + Args: + expected: The timedelta the compared value should be close to. + + Returns: + An object comparing equal to any timedelta within one microsecond of `expected`. + """ + return pytest.approx(expected, abs=timedelta(microseconds=1)) + + @pytest.fixture(autouse=True) def event_loop_policy() -> async_solipsism.EventLoopPolicy: """Return an event loop policy that uses the async solipsism event loop.""" @@ -384,7 +400,7 @@ async def test_timer_close_receiver() -> None: timer = Timer(timedelta(seconds=1.0), TriggerAllMissed()) drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.0) timer.close() @@ -403,7 +419,7 @@ async def test_timer_autostart() -> None: # time 1.0 without any drift await asyncio.sleep(0.5) drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.0) @@ -420,12 +436,12 @@ async def test_timer_autostart_with_delay() -> None: # time 1.5 without any drift await asyncio.sleep(1.2) drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.5) # Still the next tick should be at 2.5 (every second) drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(2.5) @@ -437,12 +453,12 @@ async def test_timer_autostart_with_tick_at_start() -> None: # The first tick should be at 0.0, without any delay. drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(0.0) # The next tick should be at 1.0 drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.0) @@ -460,12 +476,12 @@ async def test_timer_autostart_with_delay_and_tick_at_start() -> None: # The first tick should be at 0.5, as soon as the start delay is over. await asyncio.sleep(0.3) drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(0.5) # The next tick should be at 1.5 drift = await timer.receive() - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.5) @@ -509,7 +525,7 @@ async def test_timer_no_autostart( else: assert False, f"Unknown start method {start_method}" - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) assert event_loop.time() == pytest.approx(1.5) @@ -523,7 +539,7 @@ async def test_timer_trigger_all_missed() -> None: # We let the first tick be triggered on time drift = await timer.receive() assert event_loop.time() == pytest.approx(interval) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass interval plus a bit more, so we should get # a drift, but the next tick should be triggered still at a multiple of the @@ -531,10 +547,10 @@ async def test_timer_trigger_all_missed() -> None: await asyncio.sleep(interval + 0.1) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 2 + 0.1) - assert drift == pytest.approx(timedelta(seconds=0.1)) + assert drift == _approx_timedelta(timedelta(seconds=0.1)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 3) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass by two times the interval, so we should get # a drift of a whole interval and then next tick should be triggered @@ -542,10 +558,10 @@ async def test_timer_trigger_all_missed() -> None: await asyncio.sleep(2 * interval) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 5) - assert drift == pytest.approx(timedelta(seconds=interval)) + assert drift == _approx_timedelta(timedelta(seconds=interval)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 5) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Finally we let the time pass by 5 times the interval plus some extra # delay (even when the tolerance should be irrelevant for this mode), @@ -556,22 +572,22 @@ async def test_timer_trigger_all_missed() -> None: await asyncio.sleep(5 * interval + extra_delay) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 10 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 4 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 4 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 10 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 3 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 3 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 10 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 2 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 2 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 10 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 1 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 1 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 10 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 0 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 0 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 11) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) async def test_timer_skip_missed_and_resync() -> None: @@ -584,7 +600,7 @@ async def test_timer_skip_missed_and_resync() -> None: # We let the first tick be triggered on time drift = await timer.receive() assert event_loop.time() == pytest.approx(interval) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass interval plus a bit more, so we should get # a drift, but the next tick should be triggered still at a multiple of the @@ -592,10 +608,10 @@ async def test_timer_skip_missed_and_resync() -> None: await asyncio.sleep(interval + 0.1) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 2 + 0.1) - assert drift == pytest.approx(timedelta(seconds=0.1)) + assert drift == _approx_timedelta(timedelta(seconds=0.1)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 3) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass by two times the interval, so we should get # a drift of a whole interval and then next tick should an interval later, @@ -603,10 +619,10 @@ async def test_timer_skip_missed_and_resync() -> None: await asyncio.sleep(2 * interval) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 5) - assert drift == pytest.approx(timedelta(seconds=interval)) + assert drift == _approx_timedelta(timedelta(seconds=interval)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 6) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Finally we let the time pass by 5 times the interval plus some extra # delay. The timer should fire immediately once with a drift of 4 intervals @@ -616,13 +632,13 @@ async def test_timer_skip_missed_and_resync() -> None: await asyncio.sleep(5 * interval + extra_delay) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 11 + extra_delay) - assert drift == pytest.approx(timedelta(seconds=interval * 4 + extra_delay)) + assert drift == _approx_timedelta(timedelta(seconds=interval * 4 + extra_delay)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 12) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 13) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) async def test_timer_skip_missed_and_drift() -> None: @@ -647,10 +663,10 @@ async def test_timer_skip_missed_and_drift() -> None: await asyncio.sleep(interval + tolerance) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 2 + tolerance) - assert drift == pytest.approx(timedelta(seconds=tolerance)) + assert drift == _approx_timedelta(timedelta(seconds=tolerance)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 3) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass the interval plus two times the tolerance. Now # the timer should start to drift, and the next tick should be triggered at @@ -658,10 +674,10 @@ async def test_timer_skip_missed_and_drift() -> None: await asyncio.sleep(interval + tolerance * 2) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 4 + tolerance * 2) - assert drift == pytest.approx(timedelta(seconds=tolerance * 2)) + assert drift == _approx_timedelta(timedelta(seconds=tolerance * 2)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 5 + tolerance * 2) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Now we let the time pass by two times the interval, so we should missed # one tick (the tick at time = 6 + tolerance * 2) and the next tick should @@ -671,10 +687,10 @@ async def test_timer_skip_missed_and_drift() -> None: await asyncio.sleep(2 * interval) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 7 + tolerance * 2) - assert drift == pytest.approx(timedelta(seconds=interval)) + assert drift == _approx_timedelta(timedelta(seconds=interval)) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 8 + tolerance * 2) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) # Finally we let the time pass by 5 times the interval plus a tiny bit more # than the tolerance, so we should missed 4 ticks (the ticks at times 9+, @@ -683,10 +699,12 @@ async def test_timer_skip_missed_and_drift() -> None: await asyncio.sleep(5 * interval + tolerance + 0.001) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 13 + tolerance * 3 + 0.001) - assert drift == pytest.approx(timedelta(seconds=interval * 4 + tolerance + 0.001)) + assert drift == _approx_timedelta( + timedelta(seconds=interval * 4 + tolerance + 0.001) + ) drift = await timer.receive() assert event_loop.time() == pytest.approx(interval * 14 + tolerance * 3 + 0.001) - assert drift == pytest.approx(timedelta(seconds=0.0)) + assert drift == _approx_timedelta(timedelta(seconds=0.0)) async def test_timer_reset_with_new_interval() -> None: