Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

support hash checks for url reqs with hash fragment #735

Open
wants to merge 1 commit into from

4 participants

@qwcode
Owner

solution for #468, with unit and functional tests.

this makes pip validate the hash if you did this:

pip install http://domain.com/pkg-1.2.tar.gz#md5=fce076628d299baa2f699ac3475a674c

the complicated part in this was the possibility of using #egg and hash fragments together. I've used #egg fragments in the past when using url tar requirements to give it a definite identity in the dependency resolution process to prevent this problem: #724

@qwcode
Owner

the errors in this are caused by pypa/virtualenv#361

@qwcode
Owner

@dstufft, fyi, if you want to review this, since you opened up #468 on this awhile back.

@d1b
d1b commented

/me hopes this gets merged soon.

@g2p

It might be useful to add a flag that enforces this check. This way pip versions that are too old to compute the hash would fail the install (unrecognised flag), and a hash fragment with a syntax typo or a missing hash would also fail the install.

@msabramo

This looks like a nice enhancement. Maybe it can be revived?

@msabramo

Sounds sort of like @erikrose's peep. Could be a nice addition to pip.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 25, 2012
  1. @qwcode
This page is out of date. Refresh to see the latest.
View
2  pip/download.py
@@ -296,7 +296,7 @@ def unpack_vcs_link(link, location, only_download=False):
def unpack_file_url(link, location):
- source = url_to_path(link.url)
+ source = url_to_path(link.url_without_fragment)
content_type = mimetypes.guess_type(source)[0]
if os.path.isdir(source):
# delete the location since shutil will create it again :(
View
4 pip/index.py
@@ -648,7 +648,7 @@ def url_without_fragment(self):
scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url)
return urlparse.urlunsplit((scheme, netloc, path, query, None))
- _egg_fragment_re = re.compile(r'#egg=([^&]*)')
+ _egg_fragment_re = re.compile(r'#[^#]*egg=([^&]*)')
@property
def egg_fragment(self):
@@ -657,7 +657,7 @@ def egg_fragment(self):
return None
return match.group(1)
- _hash_re = re.compile(r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)')
+ _hash_re = re.compile(r'#[^#]*(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)')
@property
def hash(self):
View
6 pip/req.py
@@ -106,11 +106,11 @@ def from_line(cls, name, comes_from=None):
# If the line has an egg= definition, but isn't editable, pull the requirement out.
# Otherwise, assume the name is the req for the non URL/path/archive case.
if link and req is None:
- url = link.url_without_fragment
- req = link.egg_fragment #when fragment is None, this will become an 'unnamed' requirement
+ url = link.url
+ req = link.egg_fragment #when egg fragment is None, this will become an 'unnamed' requirement
# Handle relative file URLs
- if link.scheme == 'file' and re.search(r'\.\./', url):
+ if link.scheme == 'file' and re.search(r'\.\./', link.url_without_fragment):
url = path_to_url(os.path.normpath(os.path.abspath(link.path)))
else:
View
30 tests/test_index.py
@@ -129,3 +129,33 @@ def test_file_index_url_quoting():
def test_inflink_greater():
"""Test InfLink compares greater."""
assert InfLink > Link(object())
+
+
+class TestLink:
+ """Tests for the pip.index.Link"""
+
+ def test_egg_fragment(self):
+ """Test Link egg_fragment property."""
+ url = 'http://test?egg=bogus#egg=test'
+ assert Link(url).egg_fragment == 'test'
+ url = 'http://test?egg=bogus#egg=test&md5=123'
+ assert Link(url).egg_fragment == 'test'
+ url = 'http://test?egg=bogus#md5=123&egg=test'
+ assert Link(url).egg_fragment == 'test'
+
+ def test_hash_name(self):
+ """Test Link hash/hash_name properties."""
+ url = 'http://test?md5=bogus#md5=123'
+ assert Link(url).hash_name == 'md5'
+ assert Link(url).hash == '123'
+ url = 'http://test?md5=bogus#md5=123&egg=test'
+ assert Link(url).hash_name == 'md5'
+ assert Link(url).hash == '123'
+ url = 'http://test?md5=bogus#egg=test&md5=123'
+ assert Link(url).hash_name == 'md5'
+ assert Link(url).hash == '123'
+
+ def test_url_without_fragment(self):
+ """Test Link url_without_fragment property."""
+ url = 'http://test#egg=test&md5=123'
+ assert Link(url).url_without_fragment == 'http://test'
View
5 tests/test_install_requirement.py
@@ -2,9 +2,8 @@
def test_url_with_query():
- """InstallRequirement should strip the fragment, but not the query."""
+ """InstallRequirement should not strip the query."""
url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz'
fragment = '#egg=bar'
req = InstallRequirement.from_line(url + fragment)
-
- assert req.url == url, req.url
+ assert req.url.startswith(url)
View
24 tests/test_requirements.py
@@ -206,4 +206,28 @@ def test_url_req_case_mismatch():
assert egg_folder not in result.files_created, str(result)
+def test_url_req_with_correct_hash_fragment():
+ """
+ Test installing url requirement with correct md5 hash fragment succeeds.
+ """
+ env = reset_env()
+ tar = 'simple-1.0.tar.gz#md5=4bdf78ebb7911f215c1972cf71b378f0&egg=simple'
+ req_url = 'file://' + os.path.join(here, 'packages', tar)
+ result = run_pip('install', req_url)
+ egg_folder = env.site_packages / 'simple-1.0-py%s.egg-info' % pyversion
+ assert egg_folder in result.files_created, str(result)
+
+
+def test_url_req_with_incorrect_hash_fragment():
+ """
+ Test installing url requirement with incorrect md5 hash fragment fails.
+ """
+ env = reset_env()
+ tar = 'simple-1.0.tar.gz#md5=123&egg=simple'
+ req_url = 'file://' + os.path.join(here, 'packages', tar)
+ result = run_pip('install', req_url, expect_error=True, expect_temp=True)
+ 'Bad md5 hash' in result.stdout, str(result)
+
+
+
Something went wrong with that request. Please try again.