From 5348d6fa225a2a958e4f3e7fa978dde5ab03ac57 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 12 Apr 2019 01:28:17 -0700 Subject: [PATCH 01/17] Re-install PEP 508 dependencies when the URL changes From https://github.com/pypa/pip/issues/5780#issuecomment-421092322 Fixes #5780 --- src/pip/_internal/req/constructors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 7e57f22f73b..d42c32fd955 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -434,6 +434,11 @@ def install_req_from_req_string( "{} depends on {} ".format(comes_from.name, req) ) + if req.url: + return install_req_from_line( + req.url, comes_from=comes_from, isolated=isolated, + wheel_cache=wheel_cache + ) return InstallRequirement( req, comes_from, isolated=isolated, use_pep517=use_pep517 ) From 1bebc53cc557fbbb0ec80f7ee179b3d8654cdd6a Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Fri, 12 Apr 2019 01:29:42 -0700 Subject: [PATCH 02/17] Add tests for #5780 --- tests/functional/test_install.py | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index a4bacfe9447..75c27f3b3db 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1592,6 +1592,41 @@ def test_install_pep508_with_url_in_install_requires(script): assert "Successfully installed packaging-15.3" in str(res), str(res) +@pytest.mark.network +def test_install_pep508_with_url_in_install_requires_url_change(script): + pkga_path = create_test_package_with_setup( + script, name='pkga', version='1.0', + install_requires=[ + 'packaging@https://files.pythonhosted.org/packages/2f/2b/' + 'c681de3e1dbcd469537aefb15186b800209aa1f299d933d23b48d85c9d56/' + 'packaging-15.3-py2.py3-none-any.whl#sha256=' + 'ce1a869fe039fbf7e217df36c4653d1dbe657778b2d41709593a0003584405f4' + ], + ) + res = script.pip('install', pkga_path) + assert "Successfully installed packaging-15.3" in str(res), str(res) + + pkga_path.rmtree() + + # Updating the URL to the dependency installs the updated dependency + pkga_path = create_test_package_with_setup( + script, name='pkga', version='1.0', + install_requires=[ + 'packaging@https://files.pythonhosted.org/packages/91/32/' + '58bc30e646e55eab8b21abf89e353f59c0cc02c417e42929f4a9546e1b1d/' + 'packaging-19.0-py2.py3-none-any.whl#sha256=' + '9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3' + ], + ) + res = script.pip('install', pkga_path) + assert "Successfully installed packaging-19.0" in str(res), str(res) + + # The dependency is not reinstalled if the URL doesn't change + res = script.pip('install', pkga_path) + assert "Requirement already satisfied: packaging==19.0" in str(res), \ + str(res) + + @pytest.mark.network @pytest.mark.parametrize('index', (PyPI.simple_url, TestPyPI.simple_url)) def test_install_from_test_pypi_with_ext_url_dep_is_blocked(script, index): From d6d54721b7159c8c3fe1213598b3d0c5ebfb7344 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Fri, 12 Apr 2019 01:33:48 -0700 Subject: [PATCH 03/17] News fragments --- news/5780.bugfix | 1 + news/6402.bugfix | 1 + 2 files changed, 2 insertions(+) create mode 100644 news/5780.bugfix create mode 100644 news/6402.bugfix diff --git a/news/5780.bugfix b/news/5780.bugfix new file mode 100644 index 00000000000..68428c7860b --- /dev/null +++ b/news/5780.bugfix @@ -0,0 +1 @@ +Re-install PEP 508 dependencies when the URL changes diff --git a/news/6402.bugfix b/news/6402.bugfix new file mode 100644 index 00000000000..68428c7860b --- /dev/null +++ b/news/6402.bugfix @@ -0,0 +1 @@ +Re-install PEP 508 dependencies when the URL changes From d938a8e1740fc07642437eee0075307f0d8f1429 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Fri, 12 Apr 2019 01:50:57 -0700 Subject: [PATCH 04/17] Fixes for existing tests --- tests/unit/test_req_install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py index c3482d5360a..ed1bbd0aee9 100644 --- a/tests/unit/test_req_install.py +++ b/tests/unit/test_req_install.py @@ -78,7 +78,7 @@ def test_install_req_from_string_without_comes_from(self): assert isinstance(install_req, InstallRequirement) assert install_req.link.url == wheel_url - assert install_req.req.url == wheel_url + assert install_req.req.url is None assert install_req.comes_from is None assert install_req.is_wheel @@ -106,5 +106,5 @@ def test_install_req_from_string_with_comes_from_without_link(self): assert isinstance(install_req, InstallRequirement) assert install_req.comes_from.link is None assert install_req.link.url == wheel_url - assert install_req.req.url == wheel_url + assert install_req.req.url is None assert install_req.is_wheel From 51d6ab7d57393a36bd7178a9e3fe75ec5fae288b Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Wed, 17 Apr 2019 23:59:35 -0700 Subject: [PATCH 05/17] Fix news fragments --- news/5780.bugfix | 2 +- news/6402.bugfix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/news/5780.bugfix b/news/5780.bugfix index 68428c7860b..c9aa72053cf 100644 --- a/news/5780.bugfix +++ b/news/5780.bugfix @@ -1 +1 @@ -Re-install PEP 508 dependencies when the URL changes +Re-install PEP 508 dependencies when the URL changes. diff --git a/news/6402.bugfix b/news/6402.bugfix index 68428c7860b..c9aa72053cf 100644 --- a/news/6402.bugfix +++ b/news/6402.bugfix @@ -1 +1 @@ -Re-install PEP 508 dependencies when the URL changes +Re-install PEP 508 dependencies when the URL changes. From a63ca3641f69099259cc395467f347923bf4796d Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 18 Apr 2019 00:17:10 -0700 Subject: [PATCH 06/17] Rework tests to use local wheels not require network access --- tests/functional/test_install.py | 35 ++++++++++++++------------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 75c27f3b3db..62eb329ca4b 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1592,39 +1592,34 @@ def test_install_pep508_with_url_in_install_requires(script): assert "Successfully installed packaging-15.3" in str(res), str(res) -@pytest.mark.network def test_install_pep508_with_url_in_install_requires_url_change(script): - pkga_path = create_test_package_with_setup( + dep_v1_path = create_basic_wheel_for_package( + script, name='dep', version='1.0', + ) + dep_v2_path = create_basic_wheel_for_package( + script, name='dep', version='2.0', + ) + + pkga_path = create_basic_wheel_for_package( script, name='pkga', version='1.0', - install_requires=[ - 'packaging@https://files.pythonhosted.org/packages/2f/2b/' - 'c681de3e1dbcd469537aefb15186b800209aa1f299d933d23b48d85c9d56/' - 'packaging-15.3-py2.py3-none-any.whl#sha256=' - 'ce1a869fe039fbf7e217df36c4653d1dbe657778b2d41709593a0003584405f4' - ], + depends=['dep @ file://%s' % dep_v1_path], ) res = script.pip('install', pkga_path) - assert "Successfully installed packaging-15.3" in str(res), str(res) + assert "Successfully installed dep-1.0" in str(res), str(res) pkga_path.rmtree() # Updating the URL to the dependency installs the updated dependency - pkga_path = create_test_package_with_setup( - script, name='pkga', version='1.0', - install_requires=[ - 'packaging@https://files.pythonhosted.org/packages/91/32/' - '58bc30e646e55eab8b21abf89e353f59c0cc02c417e42929f4a9546e1b1d/' - 'packaging-19.0-py2.py3-none-any.whl#sha256=' - '9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3' - ], + pkga_path = create_basic_wheel_for_package( + script, name='pkga', version='2.0', + depends=['dep @ file://%s' % dep_v2_path], ) res = script.pip('install', pkga_path) - assert "Successfully installed packaging-19.0" in str(res), str(res) + assert "Successfully installed dep-2.0" in str(res), str(res) # The dependency is not reinstalled if the URL doesn't change res = script.pip('install', pkga_path) - assert "Requirement already satisfied: packaging==19.0" in str(res), \ - str(res) + assert "Requirement already satisfied: dep==2.0" in str(res), str(res) @pytest.mark.network From f7cdf3b5ec93b98c0cea750bd3da8a42fde26d1b Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 18 Apr 2019 00:37:46 -0700 Subject: [PATCH 07/17] Pass use_pep517 along --- src/pip/_internal/req/constructors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index d42c32fd955..9379e8e98ce 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -437,7 +437,7 @@ def install_req_from_req_string( if req.url: return install_req_from_line( req.url, comes_from=comes_from, isolated=isolated, - wheel_cache=wheel_cache + wheel_cache=wheel_cache, use_pep517=use_pep517 ) return InstallRequirement( req, comes_from, isolated=isolated, use_pep517=use_pep517 From 2116c048c97f1ca0693ce55ec7bfa509213a54ef Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 18 Apr 2019 02:05:15 -0700 Subject: [PATCH 08/17] PR feedback and tests --- src/pip/_internal/req/constructors.py | 2 ++ tests/functional/test_install.py | 36 +++++++++++++++++-------- tests/unit/test_req_install.py | 39 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 9379e8e98ce..8e9cbfd57b9 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -435,6 +435,8 @@ def install_req_from_req_string( ) if req.url: + # Create an InstallRequirement for a PEP 508 URL with the same behavior + # as 'pip install req.url' return install_req_from_line( req.url, comes_from=comes_from, isolated=isolated, wheel_cache=wheel_cache, use_pep517=use_pep517 diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 62eb329ca4b..6c2df81ef96 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1592,17 +1592,25 @@ def test_install_pep508_with_url_in_install_requires(script): assert "Successfully installed packaging-15.3" in str(res), str(res) -def test_install_pep508_with_url_in_install_requires_url_change(script): - dep_v1_path = create_basic_wheel_for_package( - script, name='dep', version='1.0', - ) - dep_v2_path = create_basic_wheel_for_package( - script, name='dep', version='2.0', - ) +@pytest.mark.parametrize('create_dep, format', [ + (create_test_package_with_setup, 'directory'), + (create_basic_wheel_for_package, 'wheel'), +]) +def test_install_pep508_with_url_in_install_requires_url_change( + create_dep, format, script): + dep_v1_path = create_dep(script, name='dep', version='1.0') + + if format == 'directory': + # Rename the package directory so it doesn't get overwritten when + # creating the package for dep_v2 + dep_v1_path.move(dep_v1_path.folder / 'dep_v1') + dep_v1_path = dep_v1_path.folder / 'dep_v1' + + dep_v2_path = create_dep(script, name='dep', version='2.0') pkga_path = create_basic_wheel_for_package( script, name='pkga', version='1.0', - depends=['dep @ file://%s' % dep_v1_path], + depends=['dep @ ' + path_to_url(dep_v1_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-1.0" in str(res), str(res) @@ -1612,14 +1620,20 @@ def test_install_pep508_with_url_in_install_requires_url_change(script): # Updating the URL to the dependency installs the updated dependency pkga_path = create_basic_wheel_for_package( script, name='pkga', version='2.0', - depends=['dep @ file://%s' % dep_v2_path], + depends=['dep @ ' + path_to_url(dep_v2_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-2.0" in str(res), str(res) - # The dependency is not reinstalled if the URL doesn't change res = script.pip('install', pkga_path) - assert "Requirement already satisfied: dep==2.0" in str(res), str(res) + if format == 'directory': + # pip can't determine versions from a directory name, so it will always + # reinstall the dependency + assert "Successfully installed dep-2.0" in str(res), str(res) + else: + # pip can determine the version from a wheel's filename, so the + # dependency is not reinstalled if the URL doesn't change + assert "Requirement already satisfied: dep==2.0" in str(res), str(res) @pytest.mark.network diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py index ed1bbd0aee9..3b6c721f7ce 100644 --- a/tests/unit/test_req_install.py +++ b/tests/unit/test_req_install.py @@ -8,6 +8,7 @@ from pip._internal.req.constructors import ( install_req_from_line, install_req_from_req_string, + path_to_url, ) from pip._internal.req.req_install import InstallRequirement @@ -108,3 +109,41 @@ def test_install_req_from_string_with_comes_from_without_link(self): assert install_req.link.url == wheel_url assert install_req.req.url is None assert install_req.is_wheel + + @pytest.mark.parametrize("use_pep517", [None, True, False]) + def test_install_req_from_string_pep508_url_wheel(self, use_pep517): + """ + install_req_from_string parses the version from PEP 508 URLs that point + to wheels so that updating the URL reinstalls the package. + """ + wheel_url = ("https://download.pytorch.org/whl/cu90/" + "torch-1.0.0-cp36-cp36m-win_amd64.whl") + install_str = "torch@ " + wheel_url + install_req = install_req_from_req_string( + install_str, use_pep517=use_pep517 + ) + + assert isinstance(install_req, InstallRequirement) + assert str(install_req.req) == "torch==1.0.0" + assert install_req.link.url == wheel_url + assert install_req.is_wheel + assert install_req.use_pep517 == use_pep517 + + @pytest.mark.parametrize("use_pep517", [None, True, False]) + def test_install_req_from_string_pep508_url_not_a_wheel( + self, use_pep517, tmpdir): + """ + install_req_from_string returns an InstallRequirement() with + ``.req = None`` so that the package is always reinstalled. + """ + file_url = path_to_url(tmpdir / "fake_torch_package") + install_str = "torch@ " + file_url + install_req = install_req_from_req_string( + install_str, use_pep517=use_pep517 + ) + + assert isinstance(install_req, InstallRequirement) + assert install_req.req is None + assert install_req.link.url == file_url + assert not install_req.is_wheel + assert install_req.use_pep517 == use_pep517 From 251a0025ea7bf03ec75b90091649ea647ae64e65 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 18 Apr 2019 23:35:11 -0700 Subject: [PATCH 09/17] Split the test into two tests --- tests/functional/test_install.py | 67 ++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 6c2df81ef96..037eeebbef7 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1592,21 +1592,51 @@ def test_install_pep508_with_url_in_install_requires(script): assert "Successfully installed packaging-15.3" in str(res), str(res) -@pytest.mark.parametrize('create_dep, format', [ - (create_test_package_with_setup, 'directory'), - (create_basic_wheel_for_package, 'wheel'), -]) -def test_install_pep508_with_url_in_install_requires_url_change( - create_dep, format, script): - dep_v1_path = create_dep(script, name='dep', version='1.0') +def test_install_pep508_with_url_in_install_requires_url_change_wheel(script): + dep_v1_path = create_basic_wheel_for_package( + script, name='dep', version='1.0', + ) + dep_v2_path = create_basic_wheel_for_package( + script, name='dep', version='2.0', + ) + + pkga_path = create_basic_wheel_for_package( + script, name='pkga', version='1.0', + depends=['dep @ ' + path_to_url(dep_v1_path)], + ) + res = script.pip('install', pkga_path) + assert "Successfully installed dep-1.0" in str(res), str(res) + + pkga_path.rmtree() + + # Updating the URL to the dependency installs the updated dependency + pkga_path = create_basic_wheel_for_package( + script, name='pkga', version='2.0', + depends=['dep @ ' + path_to_url(dep_v2_path)], + ) + res = script.pip('install', pkga_path) + assert "Successfully installed dep-2.0" in str(res), str(res) + + res = script.pip('install', pkga_path) + # pip can determine the version from a wheel's filename, so the + # dependency is not reinstalled if the URL doesn't change + assert "Requirement already satisfied: dep==2.0" in str(res), str(res) + - if format == 'directory': - # Rename the package directory so it doesn't get overwritten when - # creating the package for dep_v2 - dep_v1_path.move(dep_v1_path.folder / 'dep_v1') - dep_v1_path = dep_v1_path.folder / 'dep_v1' +def test_install_pep508_with_url_in_install_requires_url_change_directory( + script): + dep_v1_path = create_test_package_with_setup( + script, name='dep', version='1.0', + ) - dep_v2_path = create_dep(script, name='dep', version='2.0') + # Rename the package directory so it doesn't get overwritten when + # creating the package for dep_v2 + dep_v1_path.move(dep_v1_path.folder / 'dep_v1') + dep_v1_path = dep_v1_path.folder / 'dep_v1' + + dep_v2_path = create_test_package_with_setup( + script, name='dep', version='2.0', + ) pkga_path = create_basic_wheel_for_package( script, name='pkga', version='1.0', @@ -1626,14 +1656,9 @@ def test_install_pep508_with_url_in_install_requires_url_change( assert "Successfully installed dep-2.0" in str(res), str(res) res = script.pip('install', pkga_path) - if format == 'directory': - # pip can't determine versions from a directory name, so it will always - # reinstall the dependency - assert "Successfully installed dep-2.0" in str(res), str(res) - else: - # pip can determine the version from a wheel's filename, so the - # dependency is not reinstalled if the URL doesn't change - assert "Requirement already satisfied: dep==2.0" in str(res), str(res) + # pip can't determine versions from a directory name, so it will always + # reinstall the dependency + assert "Successfully installed dep-2.0" in str(res), str(res) @pytest.mark.network From caf1d659f54d42cc91f77361898e666643094721 Mon Sep 17 00:00:00 2001 From: lmarvaud <24732919+lmarvaud@users.noreply.github.com> Date: Thu, 10 Oct 2019 15:06:18 +0200 Subject: [PATCH 10/17] Allow extra in re-install PEP 508 dependencies when the URL changes --- src/pip/_internal/req/constructors.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 8e9cbfd57b9..63e5046db86 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -437,9 +437,17 @@ def install_req_from_req_string( if req.url: # Create an InstallRequirement for a PEP 508 URL with the same behavior # as 'pip install req.url' - return install_req_from_line( - req.url, comes_from=comes_from, isolated=isolated, - wheel_cache=wheel_cache, use_pep517=use_pep517 + parts = parse_req_from_line(req.url, None) + constraint = False + options = None + + return InstallRequirement( + parts.requirement, comes_from, link=parts.link, markers=parts.markers, + use_pep517=use_pep517, isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + constraint=constraint, + extras=req.extras, ) return InstallRequirement( req, comes_from, isolated=isolated, use_pep517=use_pep517 From a4a121a84e3439135eadfce8268619311546674a Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 10 Oct 2019 08:19:51 -0700 Subject: [PATCH 11/17] Lint --- src/pip/_internal/req/constructors.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 63e5046db86..585a2e478cd 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -442,8 +442,12 @@ def install_req_from_req_string( options = None return InstallRequirement( - parts.requirement, comes_from, link=parts.link, markers=parts.markers, - use_pep517=use_pep517, isolated=isolated, + parts.requirement, + comes_from, + link=parts.link, + markers=parts.markers, + use_pep517=use_pep517, + isolated=isolated, options=options if options else {}, wheel_cache=wheel_cache, constraint=constraint, From e3e359cd8d1ca4a03035988e47d0d07a09a2dfa1 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 10 Oct 2019 08:58:04 -0700 Subject: [PATCH 12/17] Fix existing tests --- tests/functional/test_install.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 037eeebbef7..e39578710ea 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1607,7 +1607,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_wheel(script): res = script.pip('install', pkga_path) assert "Successfully installed dep-1.0" in str(res), str(res) - pkga_path.rmtree() + pkga_path.unlink() # Updating the URL to the dependency installs the updated dependency pkga_path = create_basic_wheel_for_package( @@ -1631,8 +1631,8 @@ def test_install_pep508_with_url_in_install_requires_url_change_directory( # Rename the package directory so it doesn't get overwritten when # creating the package for dep_v2 - dep_v1_path.move(dep_v1_path.folder / 'dep_v1') - dep_v1_path = dep_v1_path.folder / 'dep_v1' + dep_v1_path.rename(dep_v1_path.parent / 'dep_v1') + dep_v1_path = dep_v1_path.parent / 'dep_v1' dep_v2_path = create_test_package_with_setup( script, name='dep', version='2.0', @@ -1645,7 +1645,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_directory( res = script.pip('install', pkga_path) assert "Successfully installed dep-1.0" in str(res), str(res) - pkga_path.rmtree() + pkga_path.unlink() # Updating the URL to the dependency installs the updated dependency pkga_path = create_basic_wheel_for_package( From 3a5f27a19615a7ed87c5b9f8061bf69666656ef9 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 10 Oct 2019 09:05:52 -0700 Subject: [PATCH 13/17] Add test for extras --- tests/unit/test_req_install.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py index 3b6c721f7ce..309370326b2 100644 --- a/tests/unit/test_req_install.py +++ b/tests/unit/test_req_install.py @@ -129,6 +129,29 @@ def test_install_req_from_string_pep508_url_wheel(self, use_pep517): assert install_req.is_wheel assert install_req.use_pep517 == use_pep517 + @pytest.mark.parametrize("use_pep517", [None, True, False]) + def test_install_req_from_string_pep508_url_wheel_extras(self, use_pep517): + """ + install_req_from_string parses the version and extras from PEP 508 URLs + that point to wheels so that updating the URL reinstalls the package. + """ + wheel_url = ( + "https://files.pythonhosted.org/packages/51/bd/" + "23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/" + "requests-2.22.0-py2.py3-none-any.whl" + ) + install_str = "requests[security] @ " + wheel_url + install_req = install_req_from_req_string( + install_str, use_pep517=use_pep517 + ) + + assert isinstance(install_req, InstallRequirement) + assert str(install_req.req) == "requests==2.22.0" + assert install_req.link.url == wheel_url + assert install_req.is_wheel + assert install_req.use_pep517 == use_pep517 + assert install_req.extras == {"security"} + @pytest.mark.parametrize("use_pep517", [None, True, False]) def test_install_req_from_string_pep508_url_not_a_wheel( self, use_pep517, tmpdir): From c348730507cf3c16eff8386ebde57d1b1195fbb0 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 9 Apr 2020 11:28:23 -0700 Subject: [PATCH 14/17] Rebase and remove bad options --- src/pip/_internal/req/constructors.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 585a2e478cd..bc10760077f 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -439,7 +439,6 @@ def install_req_from_req_string( # as 'pip install req.url' parts = parse_req_from_line(req.url, None) constraint = False - options = None return InstallRequirement( parts.requirement, @@ -448,8 +447,6 @@ def install_req_from_req_string( markers=parts.markers, use_pep517=use_pep517, isolated=isolated, - options=options if options else {}, - wheel_cache=wheel_cache, constraint=constraint, extras=req.extras, ) From 31c1f4f7ea5a4172f87ff144d5adfedad592299f Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 9 Apr 2020 12:14:30 -0700 Subject: [PATCH 15/17] Test fixes --- tests/functional/test_install.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index e39578710ea..b74a9abf709 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1602,7 +1602,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_wheel(script): pkga_path = create_basic_wheel_for_package( script, name='pkga', version='1.0', - depends=['dep @ ' + path_to_url(dep_v1_path)], + depends=['dep@' + path_to_url(dep_v1_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-1.0" in str(res), str(res) @@ -1612,7 +1612,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_wheel(script): # Updating the URL to the dependency installs the updated dependency pkga_path = create_basic_wheel_for_package( script, name='pkga', version='2.0', - depends=['dep @ ' + path_to_url(dep_v2_path)], + depends=['dep@' + path_to_url(dep_v2_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-2.0" in str(res), str(res) @@ -1640,7 +1640,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_directory( pkga_path = create_basic_wheel_for_package( script, name='pkga', version='1.0', - depends=['dep @ ' + path_to_url(dep_v1_path)], + depends=['dep@' + path_to_url(dep_v1_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-1.0" in str(res), str(res) @@ -1650,7 +1650,7 @@ def test_install_pep508_with_url_in_install_requires_url_change_directory( # Updating the URL to the dependency installs the updated dependency pkga_path = create_basic_wheel_for_package( script, name='pkga', version='2.0', - depends=['dep @ ' + path_to_url(dep_v2_path)], + depends=['dep@' + path_to_url(dep_v2_path)], ) res = script.pip('install', pkga_path) assert "Successfully installed dep-2.0" in str(res), str(res) From 2e082f916ada16ba94b851f6cb9118f3986d1ba0 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 9 Apr 2020 15:56:01 -0700 Subject: [PATCH 16/17] Limit support for directories as PEP508 dependencies --- src/pip/_internal/req/constructors.py | 29 ++++++++++---------- tests/functional/test_install.py | 38 --------------------------- tests/unit/test_req_install.py | 20 -------------- 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index bc10760077f..052ac2cabba 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -435,21 +435,20 @@ def install_req_from_req_string( ) if req.url: - # Create an InstallRequirement for a PEP 508 URL with the same behavior - # as 'pip install req.url' - parts = parse_req_from_line(req.url, None) - constraint = False - - return InstallRequirement( - parts.requirement, - comes_from, - link=parts.link, - markers=parts.markers, - use_pep517=use_pep517, - isolated=isolated, - constraint=constraint, - extras=req.extras, - ) + # Create an InstallRequirement for a wheel-like PEP 508 URL with the + # same behavior as 'pip install req.url' + parts = parse_req_from_line(req.url, comes_from) + link = Link(req.url) + if link.is_wheel: + return InstallRequirement( + parts.requirement, + comes_from=comes_from, + link=parts.link, + markers=parts.markers, + use_pep517=use_pep517, + isolated=isolated, + extras=req.extras, + ) return InstallRequirement( req, comes_from, isolated=isolated, use_pep517=use_pep517 ) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index b74a9abf709..4d03ced5be2 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1623,44 +1623,6 @@ def test_install_pep508_with_url_in_install_requires_url_change_wheel(script): assert "Requirement already satisfied: dep==2.0" in str(res), str(res) -def test_install_pep508_with_url_in_install_requires_url_change_directory( - script): - dep_v1_path = create_test_package_with_setup( - script, name='dep', version='1.0', - ) - - # Rename the package directory so it doesn't get overwritten when - # creating the package for dep_v2 - dep_v1_path.rename(dep_v1_path.parent / 'dep_v1') - dep_v1_path = dep_v1_path.parent / 'dep_v1' - - dep_v2_path = create_test_package_with_setup( - script, name='dep', version='2.0', - ) - - pkga_path = create_basic_wheel_for_package( - script, name='pkga', version='1.0', - depends=['dep@' + path_to_url(dep_v1_path)], - ) - res = script.pip('install', pkga_path) - assert "Successfully installed dep-1.0" in str(res), str(res) - - pkga_path.unlink() - - # Updating the URL to the dependency installs the updated dependency - pkga_path = create_basic_wheel_for_package( - script, name='pkga', version='2.0', - depends=['dep@' + path_to_url(dep_v2_path)], - ) - res = script.pip('install', pkga_path) - assert "Successfully installed dep-2.0" in str(res), str(res) - - res = script.pip('install', pkga_path) - # pip can't determine versions from a directory name, so it will always - # reinstall the dependency - assert "Successfully installed dep-2.0" in str(res), str(res) - - @pytest.mark.network @pytest.mark.parametrize('index', (PyPI.simple_url, TestPyPI.simple_url)) def test_install_from_test_pypi_with_ext_url_dep_is_blocked(script, index): diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py index 309370326b2..ef0ab68858a 100644 --- a/tests/unit/test_req_install.py +++ b/tests/unit/test_req_install.py @@ -8,7 +8,6 @@ from pip._internal.req.constructors import ( install_req_from_line, install_req_from_req_string, - path_to_url, ) from pip._internal.req.req_install import InstallRequirement @@ -151,22 +150,3 @@ def test_install_req_from_string_pep508_url_wheel_extras(self, use_pep517): assert install_req.is_wheel assert install_req.use_pep517 == use_pep517 assert install_req.extras == {"security"} - - @pytest.mark.parametrize("use_pep517", [None, True, False]) - def test_install_req_from_string_pep508_url_not_a_wheel( - self, use_pep517, tmpdir): - """ - install_req_from_string returns an InstallRequirement() with - ``.req = None`` so that the package is always reinstalled. - """ - file_url = path_to_url(tmpdir / "fake_torch_package") - install_str = "torch@ " + file_url - install_req = install_req_from_req_string( - install_str, use_pep517=use_pep517 - ) - - assert isinstance(install_req, InstallRequirement) - assert install_req.req is None - assert install_req.link.url == file_url - assert not install_req.is_wheel - assert install_req.use_pep517 == use_pep517 From e5bcc32444d7b602d5607716d4daaa2b21a487dc Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Thu, 9 Apr 2020 16:19:55 -0700 Subject: [PATCH 17/17] mypy fix --- src/pip/_internal/req/constructors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 052ac2cabba..fe6300cf216 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -437,7 +437,7 @@ def install_req_from_req_string( if req.url: # Create an InstallRequirement for a wheel-like PEP 508 URL with the # same behavior as 'pip install req.url' - parts = parse_req_from_line(req.url, comes_from) + parts = parse_req_from_line(req.url, None) link = Link(req.url) if link.is_wheel: return InstallRequirement(