Skip to content
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

Don't follow symlinks when uninstalling files #2552

Merged
merged 9 commits into from
Mar 22, 2015

Conversation

takluyver
Copy link
Member

I'm developing a packaging tool that, among other things, symlinks things into site-packages for development installs (i.e. similar to pip install -e). When pip tries to uninstall those things, it erroneously removes the target of the symlink instead of the symlink itself. This has been tracked for some time as issue #40, and the comments on that issue seemed to agree that resolving the symlink when uninstalling was plain wrong.

I could have fixed this in normalize_path() itself, but it may be desirable in some other places to resolve the symlink, and in most cases it will at least not be harmful. Since that function is only one line, I opted to copy its contents, minus realpath(), to where it's used.

Fixes gh-40

Crosslink pypa/flit#2.

@takluyver
Copy link
Member Author

Test failure appears to be an unrelated download failure, if someone could kick that build to restart. :-)

@qwcode
Copy link
Contributor

qwcode commented Mar 17, 2015

are these links installed as part of a project using pip? or no?

btw, as it is, I'm not aware that it's possible to install symlinks under normal circumstances, but maybe I'm wrong

@takluyver
Copy link
Member Author

takluyver commented Mar 17, 2015 via email

@takluyver
Copy link
Member Author

More broadly, though, there are various ways that someone could end up with symlinks present - the situation described in #40 was caused by something completely different, and I have created symlinks other ways as well. It would be nice if pip could avoid messing them up.

@@ -47,7 +47,7 @@ def _can_uninstall(self):
return True

def add(self, path):
path = normalize_path(path)
path = os.path.normcase(os.path.expanduser(path))
Copy link
Contributor

Choose a reason for hiding this comment

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

how about a "follow_symlink" kwarg to normalize_path

add a simple unit test for normalize_path and UninstallPathSet.add. we have no tests currently for UninstallPathSet

  • for normalize_path: tests/unit/test_utils.py
  • for add: tests/unit/test_req_uninstall.py (new module)

Copy link
Contributor

Choose a reason for hiding this comment

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

or maybe a "realpath" kwarg (default=True)

Copy link
Member Author

Choose a reason for hiding this comment

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

I called it resolve_symlinks. I considered realpath, but it seemed confusing to have parameters called 'path' and 'realpath'. I'm happy to change this to another name if you prefer.

@qwcode
Copy link
Contributor

qwcode commented Mar 18, 2015

see comment above about tests, but I agree it seems pip is wrong here.

os.symlink(f, 'file_link')

assert normalize_path('dir_link/file1', resolve_symlinks=True) \
== os.path.join(self.tempdir, f)
Copy link
Member Author

Choose a reason for hiding this comment

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

The Travis pep8 tests are failing on this line (and the similar lines below), with:

E127 continuation line over-indented for visual indent

I can't find an indentation that makes it happy - one extra space of indentation is still 'over-indented', and zero spaces gives another warning about 'missing indentation'. What is the desired formatting for a statement like this?

Copy link
Member

Choose a reason for hiding this comment

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

assert normalize_path(
    "dir_link/file1", resolve_symlinks=True,
) == os.path.join(self.tempdir, f)

Will probably work.

@takluyver
Copy link
Member Author

OK, tests added, passing and pep8 compliant. :-)

if running_under_virtualenv():
# Construct tempdir in sys.prefix, otherwise UninstallPathSet
# will reject paths.
self.tempdir = tempfile.mkdtemp(prefix=sys.prefix)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather not have the pip test suite creating temp in sys.prefix.
how about a monkeypatch for is_local instead

Copy link
Contributor

Choose a reason for hiding this comment

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

use pytest's "monkeypatch"

@qwcode
Copy link
Contributor

qwcode commented Mar 20, 2015

overall, this lgtm if you can fix those few things.

@takluyver
Copy link
Member Author

Thanks, fixed.

The remaining test failure looks like it's unrelated:

fatal: unable to connect to github.com:
github.com[0: 192.30.252.128]: errno=Connection timed out

@qwcode
Copy link
Contributor

qwcode commented Mar 22, 2015

ok, lgtm. merging.
I'll add a changelog entry after.

qwcode added a commit that referenced this pull request Mar 22, 2015
Don't follow symlinks when uninstalling files
@qwcode qwcode merged commit 39ae3c3 into pypa:develop Mar 22, 2015
qwcode added a commit that referenced this pull request Mar 22, 2015
@takluyver
Copy link
Member Author

takluyver commented Mar 22, 2015 via email

@lock lock bot added the auto-locked Outdated issues that have been locked by automation label Jun 4, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jun 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-locked Outdated issues that have been locked by automation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants