-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix rmtree failures on Windows (#1031) #1032
Fix rmtree failures on Windows (#1031) #1032
Conversation
2b0a448
to
3a37693
Compare
Rather than a fixed timeout, use an exponentially increasing timeout. This results in the ability to more quickly respond without burning CPU cycles. Use a maximum 2 second wait, as we simply repeat `rmtree`. CPython implements their own `_rmtree` allowing for tighter checks.
@sdispater, can we have this fix in the next version? I'm having to manually apply this patch on Poetry, because otherwise I cannot use Poetry in a large project I work on (since there are many dependencies, this error almost always happens in some random package during dependency resolution). |
Fix args quotation mark bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Pluckerpluck Thanks for contributing to poetry. I like this fix.
Could you please add a test. You should be able to use mocking to simulate the error.
Here is a test example. Feel free to use/modify this.
def test_robust_rmtree(mocker):
mocked_rmtree = mocker.patch('shutil.rmtree')
# should retry after exception and then work
name = tempfile.mkdtemp()
mocked_rmtree.side_effect = [OSError("Couldn't delete for some reason"), 'goodpath']
robust_rmtree(name)
# will keep retrying and then give up
name = tempfile.mkdtemp()
mocked_rmtree.side_effect = OSError("Couldn't delete for some reason")
with pytest.raises(OSError):
robust_rmtree(name, timeout=0.1)
def robust_rmtree(path, timeout=1):
"""Robustly tries to delete paths.
Retries several times if an OSError occurs.
If the final attempt fails, the Exception is propagated
to the caller.
"""
next_retry = 0.001
while next_retry < timeout:
try:
shutil.rmtree(path)
return # Only hits this on success
except OSError:
# Increase the next_retry and try again
time.sleep(next_retry)
next_retry *= 2
# Final attempt, pass any Exceptions up to caller.
shutil.rmtree(path)
to the caller. | ||
""" | ||
timeout = 0.001 | ||
while timeout < 2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why 2 seconds instead 1 like the python version? Without a strong justification otherwise, I think we should use 1.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was running 2 seconds simply becuase I felt that it wasn't an unreasonable wait that little extra longer to increase robustness when this is simply gating a crash situation (i.e. the program will error at the end of the timeout if it fails).
CPython is using it in their tests, so obviously they are much more concious of the tradeoff between robustness and speed.
That being said, 1s is almost certainly enough so I'm fine switching it to that. And we can increase it in the future if people still face the issue.
shutil.rmtree(name) | ||
|
||
def robust_rmtree(path): | ||
"""Robustly tries to delete paths. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to match the style of the codebase, please put this on a newline
I'll make the changes when I get time. Likely this weekend. |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Hello, with the changes introduced in d22afa9 there is at least a second try if something went wrong. Maybe that's enough for the problem that this PR is trying to solve? Closing this for now. @Pluckerpluck please rebase, adopt your changes and leave a comment if you think that this is still neccessary. Thanks a lot for your contribution! 👍 fin swimmer |
This still occurs with Poetry 1.1.12 on Windows :( |
When deleting temporary directories on Windows try a few times so we get around a Windows error, see issue python-poetry#1031 for more details.
Actually, the error also continues with Poetry 1.3.0 on Windows. 😞 |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Fixes #1031
Existing tests cover the code change + no need for documentation.
If you really want a test that attempts to simulate the flaky behavior for windows I can try to write one, but I can't see any sensible way to do that. So potentially change
robust_rmtree
to a private function to indicate no need for direct testing.Avoid the use of
TemporaryDirectory
, instead manually dealing with temporary files to allow custom cleanup.Retrying
shutil.rmtree
multiple times avoids the removal race conditions found in Windows causing: