diff --git a/pip/download.py b/pip/download.py index b47627ecf9d..50e02cb264c 100644 --- a/pip/download.py +++ b/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 :( diff --git a/pip/index.py b/pip/index.py index 64059bc235e..bc5c7976ef9 100644 --- a/pip/index.py +++ b/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): diff --git a/pip/req.py b/pip/req.py index 16c3cff6abc..7bde575b646 100644 --- a/pip/req.py +++ b/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: diff --git a/tests/test_index.py b/tests/test_index.py index 5c78fd7bf77..f92c5be897d 100644 --- a/tests/test_index.py +++ b/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' diff --git a/tests/test_install_requirement.py b/tests/test_install_requirement.py index dc99957dcd8..7a4735abf85 100644 --- a/tests/test_install_requirement.py +++ b/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) diff --git a/tests/test_requirements.py b/tests/test_requirements.py index a21478ac6e3..dc81909af3e 100644 --- a/tests/test_requirements.py +++ b/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) + + +