Skip to content

Commit

Permalink
Join constraints or dependencies and requested extras
Browse files Browse the repository at this point in the history
If a dependency is mentioned in constraints we just take that version,
meaning we will completely ignore any extras requested on the command
line or in a requirments file.  This takes a union of the two before
installing.

Co-Authored-By: Sachi King <[email protected]>
Closes pypa#3046, pypa#3189
  • Loading branch information
s-t-e-v-e-n-k committed Nov 10, 2015
1 parent 2f14ae5 commit 1cc36bf
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 1 deletion.
12 changes: 12 additions & 0 deletions pip/req/req_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ def add_requirement(self, install_req, parent_req_name=None):
existing_req = None
if (parent_req_name is None and existing_req and not
existing_req.constraint):
# If the extras do not match, then union them together to
# support install bar bar[foo]. If they do match, the
# InstallationError will be raised as per normal.
if existing_req.extras != install_req.extras:
existing_req.extras = tuple(
set(existing_req.extras).union(
set(install_req.extras)))
logger.debug("Setting %s extras to: %s" % (
existing_req, existing_req.extras))
return [existing_req]
raise InstallationError(
'Double requirement given: %s (already in %s, name=%r)'
% (install_req, existing_req, name))
Expand All @@ -267,6 +277,8 @@ def add_requirement(self, install_req, parent_req_name=None):
# If we're now installing a constraint, mark the existing
# object for real installation.
existing_req.constraint = False
existing_req.extras = tuple(set(existing_req.extras).union(
set(install_req.extras)))
# And now we need to scan this.
result = [existing_req]
# Canonicalise to the already-added object for the backref
Expand Down
1 change: 1 addition & 0 deletions tests/data/packages/LocalExtras-0.0.2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/LocalExtras-0.0.2.egg-info
Empty file.
29 changes: 29 additions & 0 deletions tests/data/packages/LocalExtras-0.0.2/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import os
from setuptools import setup, find_packages


def path_to_url(path):
"""
Convert a path to URI. The path will be made absolute and
will not have quoted path parts.
"""
path = os.path.normpath(os.path.abspath(path))
drive, path = os.path.splitdrive(path)
filepath = path.split(os.path.sep)
url = '/'.join(filepath)
if drive:
return 'file:///' + drive + url
return 'file://' +url


HERE = os.path.dirname(__file__)
DEP_PATH = os.path.join(HERE, '..', '..', 'indexes', 'simple', 'simple')
DEP_URL = path_to_url(DEP_PATH)

setup(
name='LocalExtras',
version='0.0.2',
packages=find_packages(),
extras_require={ 'bar': ['simple'], 'baz': ['singlemodule'] },
dependency_links=[DEP_URL]
)
2 changes: 1 addition & 1 deletion tests/data/packages/LocalExtras/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ def path_to_url(path):
name='LocalExtras',
version='0.0.1',
packages=find_packages(),
extras_require={ 'bar': ['simple'] },
extras_require={ 'bar': ['simple'], 'baz': ['singlemodule'] },
dependency_links=[DEP_URL]
)
79 changes: 79 additions & 0 deletions tests/functional/test_install_reqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,82 @@ def test_double_install_spurious_hash_mismatch(script, tmpdir):
result = script.pip_install_local(
'-r', reqs_file.abspath, expect_error=False)
assert 'Successfully installed simple-1.0' in str(result)


def test_install_with_extras_from_constraints(script, data):
to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras[bar]" % to_install
)
result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras')
assert script.site_packages / 'simple' in result.files_created


def test_install_with_extras_from_install(script, data):
to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras" % to_install
)
result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]')
assert script.site_packages / 'singlemodule.py'in result.files_created


def test_install_with_extras_joined(script, data):
to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras[bar]" % to_install
)
result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]'
)
assert script.site_packages / 'simple' in result.files_created
assert script.site_packages / 'singlemodule.py'in result.files_created


def test_install_with_extras_editable_joined(script, data):
to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write(
"-e file://%s#egg=LocalExtras[bar]" % to_install
)
result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]')
assert script.site_packages / 'simple' in result.files_created
assert script.site_packages / 'singlemodule.py'in result.files_created


def test_install_distribution_full_union(script, data):
to_install = data.packages.join("LocalExtras")
result = script.pip_install_local(
to_install, to_install + "[bar]", to_install + "[baz]")
assert 'Running setup.py install for LocalExtras' in result.stdout
assert script.site_packages / 'simple' in result.files_created
assert script.site_packages / 'singlemodule.py' in result.files_created


def test_install_distribution_duplicate_extras(script, data):
to_install = data.packages.join("LocalExtras")
package_name = to_install + "[bar]"
with pytest.raises(AssertionError):
result = script.pip_install_local(package_name, package_name)
assert 'Double requirement given: %s' % package_name in result.stderr


def test_install_distribution_union_with_constraints(script, data):
to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write(
"%s[bar]" % to_install)
result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', to_install + '[baz]')
assert 'Running setup.py install for LocalExtras' in result.stdout
assert script.site_packages / 'singlemodule.py' in result.files_created


def test_install_distribution_union_with_versions(script, data):
to_install_001 = data.packages.join("LocalExtras")
to_install_002 = data.packages.join("LocalExtras-0.0.2")
result = script.pip_install_local(
to_install_001 + "[bar]", to_install_002 + "[baz]")
assert ("Successfully installed LocalExtras-0.0.1 simple-3.0 " +
"singlemodule-0.0.1" in result.stdout)

0 comments on commit 1cc36bf

Please sign in to comment.