Comments (6)
First of all, thank you for the very detailed and well constructed bug report!
For safety and backwards compatibility I prefer the option of adding the fh.close()
before the raise
but I think that would introduce a new bug. I think that simply adding the fh.close()
at line 169 will make the next iteration of the loop inevitably fail because the file handle will be closed. So instead of capturing exceptions.LockException
I think it should capture exceptions.BaseLockException
so it also captures exceptions.AlreadyLockedException
.
I would actually prefer to refactor that code a little because it has a little bit of duplication anyhow.
As for wrapping in a finally
clause, that would still need a special case as it would also close the handle in the success state. And that would be bad :)
Dropping Python 2.7 support is something I want to do within the next year with a "final" version downloadable for people that are still stuck.
from portalocker.
I think that simply adding the
fh.close()
at line 169 will make the next iteration of the loop inevitably fail because the file handle will be closed. So instead of capturingexceptions.LockException
I think it should captureexceptions.BaseLockException
so it also capturesexceptions.AlreadyLockedException
.
Hm... if I understand the code properly, it is not supposed to catch the AlreadyLocked
exception. The exception propagates out of the function as per the API, and there would not be a next iteration.
I would actually prefer to refactor that code a little because it has a little bit of duplication anyhow.
I did feel an urge to do some refactoring. Butm well, obligatory spacebar and heater xkcd!
If you intend to refactor a little, I would like to ask: is it necessary to wait for one iteration to raise AlreadyLocked
? The fail_when_locked
variable is essentially constant inside the function. So the exception is either raised in the first iteration, or it never will. In particular, it would make sense to pull it above the while loop, unless it is intentional to wait one iteration first. From reading the acquire
docstring (copied below), perhaps it was to ensure other locking threads have a chance to finish locking?
fail_when_locked is useful when multiple threads/processes can race
when creating a file. If set to true than the system will wait till
the lock was acquired and then return an AlreadyLocked exception.
As for wrapping in a
finally
clause, that would still need a special case as it would also close the handle in the success state. And that would be bad :)
Yes, I agree! It does feel a little off that the preferred path requires special handling.
Dropping Python 2.7 support is something I want to do within the next year with a "final" version downloadable for people that are still stuck.
Nice! May be type hinting will come in too!
Edit: Here is a suggestion that puts the exception checking in one place. It still goes checks fail_when_locked
every loop though.
# Set the timeout end time as soon as possible.
timeout_end = current_time() + timeout
# Get a new filehandler and lock.
# Ensure handle is closed unless file is locked and prepared.
try:
fh = self._get_fh()
# Retry till the timeout has passed.
# A do-while style loop to always try once.
sleep = time.sleep
while True:
try:
self._get_lock(fh)
except exceptions.LockException as exception:
# Breaking: no sleep if `fail_when_locked`.
if fail_when_locked:
# TODO[Python 3]: Use `raise ... from exception`.
raise exceptions.AlreadyLocked(exception)
if timeout_end <= current_time():
raise
# Retry if not timed out and no fail_when_locked.
# Leave the `try` scope as soon as possible.
else:
# We've got the lock.
# Prepare the filehandle (truncate if needed).
# If `_prepare_fh` fails, we still close the handle.
self.fh = self._prepare_fh(fh)
return fh
# Wait a bit and retry.
sleep(check_interval)
finally:
if self.fh is None: # pragma: no branch # Returns if True.
fh.close() # pragma: no branch # No return.
from portalocker.
I believe the lastest commit addca52 has fixed this. I will leave closing this issue to you, in case you want to do that only when merging to the main branch.
And thank you for your excellent work on this. It is a lot more readable (to me) now. I have even learnt that except
clauses del
the assigned exc
variable. That is surprising to me! I had to look this up.
from portalocker.
Yes, I'll close it with the new release. Should be out somewhere this week :)
The exception thing is indeed a tricky one, you'll never know it went wrong until it bites you. It's a "feature" that was introduced in Python 3, Python 2 didn't have that issue. I discovered it several years ago when doing research for my Python book :)
For future reference, even though you would expect it to work, this does not work:
exception = None
try:
...
except Something as exception:
# `exception` is still available here
....
# `exception` is not available here
from portalocker.
The new release is now online. It also includes type hinting and it has a distributed redis lock included.
from portalocker.
And thank you for all the help of course :)
from portalocker.
Related Issues (20)
- Rlock - with limit? HOT 6
- What are the different locking mechanisms? HOT 2
- Declare PEP 561 compliance (type checking support) HOT 3
- Sorry for OT HOT 2
- Neither exclusive nor shared locks working as expected on Windows HOT 2
- Windows shared lock with Python 3 HOT 11
- redis submodule cannot be imported due to circular import HOT 4
- race condition for⦠file metadata? HOT 4
- Setting the DenyMode against a file HOT 2
- is lock reenterable? HOT 2
- distutils was depreacted in Python 3.10 HOT 2
- Timeout not working with SHARED flag HOT 4
- Better Examples Needed HOT 3
- LockException(OSError(22, 'Invalid argument')) when using NON_BLOCKING flag on Linux HOT 2
- DLL load failed while importing win32file HOT 1
- What is the actual license for this project? HOT 1
- fileno support HOT 2
- Mismatch between documentation and implementation HOT 3
- How to lock a file but be able to read it HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from portalocker.