11import logging
22import time
3+ from abc import ABC , abstractmethod
34from threading import Lock
5+ from types import TracebackType
6+ from typing import Optional , Type , Union
47
58from ._error import Timeout
69
1114# This is a helper class which is returned by :meth:`BaseFileLock.acquire` and wraps the lock to make sure __enter__
1215# is not called twice when entering the with statement. If we would simply return *self*, the lock would be acquired
1316# again in the *__enter__* method of the BaseFileLock, but not released again automatically. issue #37 (memory leak)
14- class AcquireReturnProxy ( object ) :
17+ class AcquireReturnProxy :
1518 """A context aware object that will release the lock file when exiting."""
1619
17- def __init__ (self , lock ) :
20+ def __init__ (self , lock : "BaseFileLock" ) -> None :
1821 self .lock = lock
1922
20- def __enter__ (self ):
23+ def __enter__ (self ) -> "BaseFileLock" :
2124 return self .lock
2225
23- def __exit__ (self , exc_type , exc_value , traceback ): # noqa: U100
26+ def __exit__ (
27+ self ,
28+ exc_type : Optional [Type [BaseException ]], # noqa: U100
29+ exc_value : Optional [BaseException ], # noqa: U100
30+ traceback : Optional [TracebackType ], # noqa: U100
31+ ) -> None :
2432 self .lock .release ()
2533
2634
27- class BaseFileLock (object ):
35+ class BaseFileLock (ABC ):
2836 """Abstract base class for a file lock object."""
2937
30- def __init__ (self , lock_file , timeout = - 1 ):
38+ def __init__ (self , lock_file : str , timeout : float = - 1 ) -> None :
3139 """
3240 Create a new lock object.
3341
@@ -37,29 +45,29 @@ def __init__(self, lock_file, timeout=-1):
3745 A timeout of 0 means, that there is exactly one attempt to acquire the file lock.
3846 """
3947 # The path to the lock file.
40- self ._lock_file = lock_file
48+ self ._lock_file : str = lock_file
4149
4250 # The file descriptor for the *_lock_file* as it is returned by the os.open() function.
4351 # This file lock is only NOT None, if the object currently holds the lock.
44- self ._lock_file_fd = None
52+ self ._lock_file_fd : Optional [ int ] = None
4553
4654 # The default timeout value.
47- self .timeout = timeout
55+ self .timeout : float = timeout
4856
4957 # We use this lock primarily for the lock counter.
50- self ._thread_lock = Lock ()
58+ self ._thread_lock : Lock = Lock ()
5159
5260 # The lock counter is used for implementing the nested locking mechanism. Whenever the lock is acquired, the
5361 # counter is increased and the lock is only released, when this value is 0 again.
54- self ._lock_counter = 0
62+ self ._lock_counter : int = 0
5563
5664 @property
57- def lock_file (self ):
65+ def lock_file (self ) -> str :
5866 """:return: path to the lock file"""
5967 return self ._lock_file
6068
6169 @property
62- def timeout (self ):
70+ def timeout (self ) -> float :
6371 """
6472 :return: the default timeout value
6573
@@ -68,24 +76,26 @@ def timeout(self):
6876 return self ._timeout
6977
7078 @timeout .setter
71- def timeout (self , value ) :
79+ def timeout (self , value : Union [ float , str ]) -> None :
7280 """
7381 Change the default timeout value.
7482
7583 :param value: the new value
7684 """
7785 self ._timeout = float (value )
7886
79- def _acquire (self ):
87+ @abstractmethod
88+ def _acquire (self ) -> None :
8089 """If the file lock could be acquired, self._lock_file_fd holds the file descriptor of the lock file."""
8190 raise NotImplementedError
8291
83- def _release (self ):
92+ @abstractmethod
93+ def _release (self ) -> None :
8494 """Releases the lock and sets self._lock_file_fd to None."""
8595 raise NotImplementedError
8696
8797 @property
88- def is_locked (self ):
98+ def is_locked (self ) -> bool :
8999 """
90100
91101 :return: A boolean indicating if the lock file is holding the lock currently.
@@ -96,7 +106,7 @@ def is_locked(self):
96106 """
97107 return self ._lock_file_fd is not None
98108
99- def acquire (self , timeout = None , poll_intervall = 0.05 ):
109+ def acquire (self , timeout : Optional [ float ] = None , poll_intervall : float = 0.05 ) -> AcquireReturnProxy :
100110 """
101111 Try to acquire the file lock.
102112
@@ -160,7 +170,7 @@ def acquire(self, timeout=None, poll_intervall=0.05):
160170 raise
161171 return AcquireReturnProxy (lock = self )
162172
163- def release (self , force = False ):
173+ def release (self , force : bool = False ) -> None :
164174 """
165175 Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0. Also
166176 note, that the lock file itself is not automatically deleted.
@@ -180,7 +190,7 @@ def release(self, force=False):
180190 self ._lock_counter = 0
181191 _LOGGER .debug ("Lock %s released on %s" , lock_id , lock_filename )
182192
183- def __enter__ (self ):
193+ def __enter__ (self ) -> "BaseFileLock" :
184194 """
185195 Acquire the lock.
186196
@@ -189,7 +199,12 @@ def __enter__(self):
189199 self .acquire ()
190200 return self
191201
192- def __exit__ (self , exc_type , exc_value , traceback ): # noqa: U100
202+ def __exit__ (
203+ self ,
204+ exc_type : Optional [Type [BaseException ]], # noqa: U100
205+ exc_value : Optional [BaseException ], # noqa: U100
206+ traceback : Optional [TracebackType ], # noqa: U100
207+ ) -> None :
193208 """
194209 Release the lock.
195210
@@ -199,7 +214,7 @@ def __exit__(self, exc_type, exc_value, traceback): # noqa: U100
199214 """
200215 self .release ()
201216
202- def __del__ (self ):
217+ def __del__ (self ) -> None :
203218 """Called when the lock object is deleted."""
204219 self .release (force = True )
205220
0 commit comments