Skip to content

bpo-36656: Add race-free os.symlink wrapper / helper #14464

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

Open
wants to merge 161 commits into
base: main
Choose a base branch
from

Conversation

HaleTom
Copy link

@HaleTom HaleTom commented Jun 29, 2019

bpo-36656: Add race-free os.symlink wrapper / helper

Based on the positive support of Inada Naoki and Serhiy Storchaka here:

https://code.activestate.com/lists/python-ideas/56421/

I offer this PR as a proposed implementation for shutil.symlink.

Notes

  • I don't have a Windows licence to run the tests under that OS. Feedback and fixes for any breaking tests is requested.
  • shutil._create_or_replace is called by both shutil.link and shutil.symlink, the latter two being simple wrappers. _create_or_replace does the work of assuring that if a destination already exists, it is atomically replaced.

Related BPO

This PR is an enabler to resolve the symlink overwrite issue:
bpo-41134: distutils.dir_util.copy_tree FileExistsError when updating symlinks

Prior art

python-ideas discussion: https://code.activestate.com/lists/python-ideas/55992/

https://bugs.python.org/issue36656

@the-knights-who-say-ni
Copy link

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue.

You can check yourself to see if the CLA has been received.

Thanks again for your contribution, we look forward to reviewing it!

@solvingj
Copy link

solvingj commented Dec 6, 2019

This is so long overdue. I would suggest to use the implementation here as well.

https://github.com/python/cpython/blob/master/Lib/distutils/dir_util.py#L152

@solvingj
Copy link

solvingj commented Dec 9, 2019

Until this is merged, and for those who need support for this kind of thing in older pythons, here's an example of how to workaround it with monkeypatching:
https://stackoverflow.com/questions/53090360/python-distutils-copy-tree-fails-to-update-if-there-are-symlinks

You can replace the provided implementation with the race-free logic @HaleTom provided if necessary.

@HaleTom
Copy link
Author

HaleTom commented Dec 11, 2019

@solvingj thanks for drawing my attention back to this, and yet another reason to implement it.

This is my first contribution to cpython, and I'll be asking on the tutor mail list on how to proceed.

Please feel free to poke me if you don't see any activity within 2 weeks.

Copy link
Member

@terryjreedy terryjreedy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blurb needed. This did not make 3.8 or 3.9, so 3.10 is soonest for what's new.
Move todo to issue.
Reread https://www.python.org/dev/peps/pep-0008/
In particular https://www.python.org/dev/peps/pep-0008/#blank-lines
"Method definitions inside a class are surrounded by a single blank line."

@@ -1138,6 +1138,10 @@ The new :func:`shlex.join` function acts as the inverse of :func:`shlex.split`.
shutil
------

Added :func:`shutil.link` and :func:`shutil.symlink` which allow for multiple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this entry to to a blurb. One is required. Since we are well before the 3.10 cutoff next May/June, maybe to 3.10.rst, but not required.

Copy link
Author

@HaleTom HaleTom Jul 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@terryjreedy w.r.t the blank lines, I think that my additions to shutil.py are covered by:

Surround top-level function and class definitions with two blank lines.

So, given this, (and flake8's complaints), I intend to revert the commit I made to reduce them to only one.

Please tell me if I've misunderstood or if you were referring to test_shutil.py instead, where I've used the following to group tests:

Extra blank lines may be used (sparingly) to separate groups of related functions.

I used 8 extra blank lines for functional area grouping.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@terryjreedy
Copy link
Member

terryjreedy commented Jul 23, 2020

Did you originally create shutil-links branch from master?
If so, I strongly recommend keeping up with master with git merge upstream/master in the shutil-links branch.
Do the tests run on your local machine? Or rather, if you run tests on master, switch to shutil-links branch, and re-run, do you see more failures? (Some tests may fail locally that pass on CI.) If there are intermittant local failures, run at least twice on each branch.

Did you analyze all of the failures (no longer accessible from here) to see if related to patch? Is there any way patch could affect builds? If not, there could be intermittent unrelated problem. I just got a clean 'all pass' with a trivial PR.

import tempfile
try:
# Loop until successful creation of previously non-existent temp pathname
while True:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use mkdtemp and create the temp file inside of that dir, instead of the loop?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants