diff --git a/pip/req/req_install.py b/pip/req/req_install.py index 7c5bf8f4458..d807c2080cd 100644 --- a/pip/req/req_install.py +++ b/pip/req/req_install.py @@ -1065,6 +1065,8 @@ def parse_editable(editable_req, default_vcs=None): .[some_extra] """ + from pip.index import Link + url = editable_req extras = None @@ -1086,9 +1088,10 @@ def parse_editable(editable_req, default_vcs=None): url_no_extras = path_to_url(url_no_extras) if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment if extras: return ( - None, + package_name, url_no_extras, pkg_resources.Requirement.parse( '__placeholder__' + extras @@ -1096,7 +1099,7 @@ def parse_editable(editable_req, default_vcs=None): {}, ) else: - return None, url_no_extras, None, {} + return package_name, url_no_extras, None, {} for version_control in vcs: if url.lower().startswith('%s:' % version_control): diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 7070a71bc2d..58951adba96 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -253,6 +253,13 @@ def add_requirement(self, install_req, parent_req_name=None): # scanning. result = [] elif not install_req.constraint: + if (install_req.link and not (existing_req.link and + install_req.link.path == existing_req.link.path)): + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name) # If we're now installing a constraint, mark the existing # object for real installation. existing_req.constraint = False @@ -267,9 +274,12 @@ def add_requirement(self, install_req, parent_req_name=None): return result def has_requirement(self, project_name): - for name in project_name, project_name.lower(): - if name in self.requirements or name in self.requirement_aliases: - return True + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True return False @property diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py index 2a5005abad9..f1ba71b1b71 100644 --- a/tests/functional/test_install_reqs.py +++ b/tests/functional/test_install_reqs.py @@ -256,3 +256,60 @@ def test_constraints_only_causes_error(script, data): 'install', '--no-index', '-f', data.find_links, '-c', script.scratch_path / 'c.txt', expect_error=True) assert 'installed requiresupper' not in result.stdout + + +def test_constraints_local_editable_install_causes_error(script, data): + script.scratch_path.join("constraints.txt").write( + "singlemodule==0.0.0" + ) + to_install = data.src.join("singlemodule") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', '-e', + to_install, expect_error=True) + assert 'Could not satisfy constraints for' in result.stderr + + +def test_constraints_local_install_causes_error(script, data): + script.scratch_path.join("constraints.txt").write( + "singlemodule==0.0.0" + ) + to_install = data.src.join("singlemodule") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', + to_install, expect_error=True) + assert 'Could not satisfy constraints for' in result.stderr + + +def test_constraints_constrain_to_local_editable(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "-e file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', 'singlemodule') + assert 'Running setup.py develop for singlemodule' in result.stdout + + +def test_constraints_constrain_to_local(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', 'singlemodule') + assert 'Running setup.py install for singlemodule' in result.stdout + + +def test_constrained_to_url_install_same_url(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', to_install) + assert 'Running setup.py install for singlemodule' in result.stdout