From 603ccb7ecf037c2c24afb8f1d0f7f2de43eb89e9 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sat, 19 Sep 2026 16:19:12 +0100 Subject: [PATCH] chore(typing): Fix all mypy deprecated warnings Note: Depends on #823 --- .coveragerc | 7 --- kazoo/protocol/connection.py | 8 ++- kazoo/recipe/lease.py | 20 +++----- kazoo/tests/test_client.py | 4 +- kazoo/tests/test_lease.py | 95 ++++++++++++------------------------ pyproject.toml | 5 +- 6 files changed, 47 insertions(+), 92 deletions(-) diff --git a/.coveragerc b/.coveragerc index c79b53748..d84a6fc8b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,10 +4,3 @@ include = omit = kazoo/tests/* kazoo/testing/* - -# Note - this is a copy of the default exclusions from coverage 7.10.1 -[report] -exclude_lines = - #\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER) - ^\s*(((async )?def .*?)?\)(\s*->.*?)?:\s*)?\.\.\.\s*(#|$) - if (typing\.)?TYPE_CHECKING: diff --git a/kazoo/protocol/connection.py b/kazoo/protocol/connection.py index d35952be5..cce1e8646 100644 --- a/kazoo/protocol/connection.py +++ b/kazoo/protocol/connection.py @@ -461,8 +461,12 @@ def _read_watch_event(self, buffer: bytes, offset: int) -> None: watchers.extend(client._child_watchers.pop(path, [])) elif watch.type == CHILD_EVENT: watchers.extend(client._child_watchers.pop(path, [])) - else: - self.logger.warn("Received unknown event %r", watch.type) + else: # pragma: no cover + # No coverage here because because it'd require an unknown event + # type to be sent from the server. We could do a unit test that + # calls this method directly with a fake event, but that would be + # fragile and not really test the code in a meaningful way. + self.logger.warning("Received unknown event %r", watch.type) return # Strip the chroot if needed diff --git a/kazoo/recipe/lease.py b/kazoo/recipe/lease.py index ace931839..46e5b0e26 100644 --- a/kazoo/recipe/lease.py +++ b/kazoo/recipe/lease.py @@ -11,7 +11,7 @@ import datetime import json import socket -from typing import Callable, TypedDict, TYPE_CHECKING, cast +from typing import TypedDict, TYPE_CHECKING, cast from kazoo.exceptions import CancelledError @@ -55,8 +55,8 @@ class NonBlockingLease: """ # Bump when storage format changes - _version = 1 - _date_format = "%Y-%m-%dT%H:%M:%S" + _version = 2 + _date_format = "%Y-%m-%dT%H:%M:%S%z" _byte_encoding = "utf-8" def __init__( @@ -65,7 +65,6 @@ def __init__( path: str, duration: datetime.timedelta, identifier: str | None = None, - utcnow: Callable[[], datetime.datetime] = datetime.datetime.utcnow, ): """Create a non-blocking lease. @@ -76,13 +75,11 @@ def __init__( :param identifier: Unique name to use for this lease holder. Reuse in order to renew the lease. Defaults to :meth:`socket.gethostname()`. - :param utcnow: Clock function, by default returning - :meth:`datetime.datetime.utcnow()`. Used for testing. - """ + ident = identifier or socket.gethostname() self.obtained = False - self._attempt_obtaining(client, path, duration, ident, utcnow) + self._attempt_obtaining(client, path, duration, ident) def _attempt_obtaining( self, @@ -90,14 +87,13 @@ def _attempt_obtaining( path: str, duration: datetime.timedelta, ident: str, - utcnow: Callable[[], datetime.datetime], ) -> None: client.ensure_path(path) holder_path = path + "/lease_holder" lock = client.Lock(path, ident) try: with lock: - now = utcnow() + now = datetime.datetime.now(datetime.timezone.utc) if client.exists(holder_path): raw, _ = client.get(holder_path) data = self._decode(raw) @@ -148,8 +144,6 @@ class MultiNonBlockingLease: :param identifier: Unique name to use for this lease holder. Reuse in order to renew the lease. Defaults do :meth:`socket.gethostname()`. - :param utcnow: Clock function, by default returning - :meth:`datetime.datetime.utcnow()`. Used for testing. """ @@ -160,7 +154,6 @@ def __init__( path: str, duration: datetime.timedelta, identifier: str | None = None, - utcnow: Callable[[], datetime.datetime] = datetime.datetime.utcnow, ): self.obtained = False for num in range(count): @@ -169,7 +162,6 @@ def __init__( "%s/%d" % (path, num), duration, identifier=identifier, - utcnow=utcnow, ) if ls: self.obtained = True diff --git a/kazoo/tests/test_client.py b/kazoo/tests/test_client.py index c0263c1ae..176d596c2 100644 --- a/kazoo/tests/test_client.py +++ b/kazoo/tests/test_client.py @@ -4,7 +4,6 @@ import socket import tempfile import threading -import time import uuid import unittest @@ -688,7 +687,8 @@ def test_create_makepath_incompatible_acls(self) -> None: credential = make_digest_acl_credential("username", "password") alt_client = KazooClient( self.cluster[0].address + self.client.chroot, - max_retries=5, + connection_retry=KazooRetry(max_tries=5), + command_retry=KazooRetry(max_tries=5), auth_data=[("digest", credential)], handler=self._makeOne(), ) diff --git a/kazoo/tests/test_lease.py b/kazoo/tests/test_lease.py index cef9d85a6..a523d6ef8 100644 --- a/kazoo/tests/test_lease.py +++ b/kazoo/tests/test_lease.py @@ -3,23 +3,14 @@ import datetime import uuid +from freezegun import freeze_time + from kazoo.recipe.lease import NonBlockingLease from kazoo.recipe.lease import MultiNonBlockingLease from kazoo.testing import KazooTestCase -class MockClock: - def __init__(self, epoch: float = 0): - self.epoch = epoch - - def forward(self, seconds: float) -> None: - self.epoch += seconds - - def __call__(self) -> datetime.datetime: - return datetime.datetime.utcfromtimestamp(self.epoch) - - class KazooLeaseTests(KazooTestCase): def setUp(self) -> None: super().setUp() @@ -28,7 +19,10 @@ def setUp(self) -> None: self.client3 = self._get_client(timeout=0.8) self.client3.start() self.path = "/" + uuid.uuid4().hex - self.clock = MockClock(10) + # From python3.11 this should use enterContext + self._frozen_time = freeze_time() + self._clock = self._frozen_time.start() + self.addCleanup(self._frozen_time.stop) def tearDown(self) -> None: for cl in [self.client2, self.client3]: @@ -45,14 +39,14 @@ def test_renew(self) -> None: # class directly in # other tests in order to get better IDE support. lease = self.client.NonBlockingLease( - self.path, datetime.timedelta(seconds=3), utcnow=self.clock + self.path, datetime.timedelta(seconds=3) ) assert lease assert lease.obtained is True - self.clock.forward(2) + self._clock.tick(2) renewed_lease = self.client.NonBlockingLease( - self.path, datetime.timedelta(seconds=3), utcnow=self.clock + self.path, datetime.timedelta(seconds=3) ) assert renewed_lease @@ -61,17 +55,15 @@ def test_busy(self) -> None: self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert lease - self.clock.forward(2) + self._clock.tick(2) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert not foreigner_lease assert foreigner_lease.obtained is False @@ -81,40 +73,37 @@ def test_overtake(self) -> None: self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert lease - self.clock.forward(4) + self._clock.tick(4) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert foreigner_lease def test_renew_no_overtake(self) -> None: lease = self.client.NonBlockingLease( - self.path, datetime.timedelta(seconds=3), utcnow=self.clock + self.path, datetime.timedelta(seconds=3) ) assert lease assert lease.obtained is True - self.clock.forward(2) + self._clock.tick(2) renewed_lease = self.client.NonBlockingLease( - self.path, datetime.timedelta(seconds=3), utcnow=self.clock + self.path, datetime.timedelta(seconds=3) ) assert renewed_lease - self.clock.forward(2) + self._clock.tick(2) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert not foreigner_lease @@ -123,27 +112,24 @@ def test_overtaker_renews(self) -> None: self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert lease - self.clock.forward(4) + self._clock.tick(4) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert foreigner_lease - self.clock.forward(2) + self._clock.tick(2) foreigner_renew = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert foreigner_renew @@ -152,26 +138,23 @@ def test_overtake_refuse_first(self) -> None: self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert lease - self.clock.forward(4) + self._clock.tick(4) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) assert foreigner_lease - self.clock.forward(2) + self._clock.tick(2) first_again_lease = NonBlockingLease( self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert not first_again_lease @@ -182,19 +165,17 @@ def test_old_version(self) -> None: self.client, self.path, datetime.timedelta(seconds=3), - utcnow=self.clock, ) assert lease # Then back to today. NonBlockingLease._version -= 1 - self.clock.forward(4) + self._clock.tick(4) foreigner_lease = NonBlockingLease( self.client2, self.path, datetime.timedelta(seconds=3), identifier="some.other.host", - utcnow=self.clock, ) # Since a newer version wrote the lease file, the lease is not taken. assert not foreigner_lease @@ -203,16 +184,15 @@ def test_old_version(self) -> None: class MultiNonBlockingLeaseTest(KazooLeaseTests): def test_1_renew(self) -> None: ls = self.client.MultiNonBlockingLease( - 1, self.path, datetime.timedelta(seconds=4), utcnow=self.clock + 1, self.path, datetime.timedelta(seconds=4) ) assert ls - self.clock.forward(2) + self._clock.tick(2) ls2 = MultiNonBlockingLease( self.client, 1, self.path, datetime.timedelta(seconds=4), - utcnow=self.clock, ) assert ls2 @@ -222,17 +202,15 @@ def test_1_reject(self) -> None: 1, self.path, datetime.timedelta(seconds=4), - utcnow=self.clock, ) assert ls - self.clock.forward(2) + self._clock.tick(2) ls2 = MultiNonBlockingLease( self.client2, 1, self.path, datetime.timedelta(seconds=4), identifier="some.other.host", - utcnow=self.clock, ) assert not ls2 @@ -242,36 +220,32 @@ def test_2_renew(self) -> None: 2, self.path, datetime.timedelta(seconds=7), - utcnow=self.clock, ) assert ls - self.clock.forward(2) + self._clock.tick(2) ls2 = MultiNonBlockingLease( self.client2, 2, self.path, datetime.timedelta(seconds=7), identifier="host2", - utcnow=self.clock, ) assert ls2 - self.clock.forward(2) + self._clock.tick(2) ls3 = MultiNonBlockingLease( self.client, 2, self.path, datetime.timedelta(seconds=7), - utcnow=self.clock, ) assert ls3 - self.clock.forward(2) + self._clock.tick(2) ls4 = MultiNonBlockingLease( self.client2, 2, self.path, datetime.timedelta(seconds=7), identifier="host2", - utcnow=self.clock, ) assert ls4 @@ -281,27 +255,24 @@ def test_2_reject(self) -> None: 2, self.path, datetime.timedelta(seconds=7), - utcnow=self.clock, ) assert ls - self.clock.forward(2) + self._clock.tick(2) ls2 = MultiNonBlockingLease( self.client2, 2, self.path, datetime.timedelta(seconds=7), identifier="host2", - utcnow=self.clock, ) assert ls2 - self.clock.forward(2) + self._clock.tick(2) ls3 = MultiNonBlockingLease( self.client3, 2, self.path, datetime.timedelta(seconds=7), identifier="host3", - utcnow=self.clock, ) assert not ls3 @@ -311,35 +282,31 @@ def test_2_handover(self) -> None: 2, self.path, datetime.timedelta(seconds=4), - utcnow=self.clock, ) assert ls - self.clock.forward(2) + self._clock.tick(2) ls2 = MultiNonBlockingLease( self.client2, 2, self.path, datetime.timedelta(seconds=4), identifier="host2", - utcnow=self.clock, ) assert ls2 - self.clock.forward(3) + self._clock.tick(3) ls3 = MultiNonBlockingLease( self.client3, 2, self.path, datetime.timedelta(seconds=4), identifier="host3", - utcnow=self.clock, ) assert ls3 - self.clock.forward(2) + self._clock.tick(2) ls4 = MultiNonBlockingLease( self.client, 2, self.path, datetime.timedelta(seconds=4), - utcnow=self.clock, ) assert ls4 diff --git a/pyproject.toml b/pyproject.toml index 9455bf328..9f942daf3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ ruff = [ ] test-extras = [ "cryptography==50.0.1", + "freezegun==1.5.5", "objgraph==3.6.2", "pyjks==20.0.0", "unittest-parametrize==1.8.0", @@ -177,9 +178,7 @@ warn_unreachable = true # Miscellaneous strictness flags allow_untyped_globals = false allow_redefinition = false -# FIXME Disabled for now - there's a few things to clean up (mostly using -# deprecated APIs in tests) before we can enable this. -#enable_error_code = ["deprecated"] +enable_error_code = ["deprecated"] local_partial_types = true implicit_reexport = false strict_concatenate = true