@@ -843,6 +843,37 @@ def test_lock_acquire_none(self, etcd):
843843 # lock is not refreshed
844844 assert lock .acquire (None ) is True
845845
846+ def test_lock_acquire_with_timeout (self , etcd ):
847+ lock1 = etcd .lock ('lock-10' , ttl = 10 )
848+ lock2 = etcd .lock ('lock-10' , ttl = 10 )
849+
850+ original_watch_once = etcd .watch_once
851+ watch_once_called = [0 ]
852+
853+ def release_lock_before_watch (* args , ** kwargs ):
854+ watch_once_called [0 ] += 1
855+ # Simulates the case where key is expired before watch is called.
856+ # See https://github.com/kragniz/python-etcd3/issues/1107
857+ lock1 .release ()
858+ return original_watch_once (* args , ** kwargs )
859+
860+ original_transaction = etcd .transaction
861+ transaction_called = [0 ]
862+
863+ def transaction_wrapper (* args , ** kwargs ):
864+ transaction_called [0 ] += 1
865+ return original_transaction (* args , ** kwargs )
866+
867+ assert lock1 .acquire () is True
868+ with mock .patch .object (etcd3 .Etcd3Client , 'watch_once' ,
869+ wraps = release_lock_before_watch ):
870+ with mock .patch .object (etcd3 .Etcd3Client , 'transaction' ,
871+ wraps = transaction_wrapper ):
872+ assert lock2 .acquire (timeout = 5 ) is True
873+
874+ assert watch_once_called [0 ] == 1
875+ assert transaction_called [0 ] == 2
876+
846877 def test_internal_exception_on_internal_error (self , etcd ):
847878 exception = self .MockedException (grpc .StatusCode .INTERNAL )
848879 kv_mock = mock .MagicMock ()
0 commit comments