mirror of https://github.com/pypa/pip
Merge branch 'refactor/reduce-action-at-distance' into resolver/warn-after-resolution
This commit is contained in:
commit
5ae396d188
|
@ -281,35 +281,37 @@ class RequirementCommand(Command):
|
|||
# requirement_set.require_hashes may be updated
|
||||
|
||||
for filename in options.constraints:
|
||||
for req in parse_requirements(
|
||||
for req_to_add in parse_requirements(
|
||||
filename,
|
||||
constraint=True, finder=finder, options=options,
|
||||
session=session, wheel_cache=wheel_cache):
|
||||
requirement_set.add_requirement(req)
|
||||
req_to_add.is_direct = True
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
|
||||
for req in args:
|
||||
requirement_set.add_requirement(
|
||||
InstallRequirement.from_line(
|
||||
req, None, isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
)
|
||||
req_to_add = InstallRequirement.from_line(
|
||||
req, None, isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
)
|
||||
req_to_add.is_direct = True
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
|
||||
for req in options.editables:
|
||||
requirement_set.add_requirement(
|
||||
InstallRequirement.from_editable(
|
||||
req,
|
||||
isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
)
|
||||
req_to_add = InstallRequirement.from_editable(
|
||||
req,
|
||||
isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
)
|
||||
req_to_add.is_direct = True
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
|
||||
for filename in options.requirements:
|
||||
for req in parse_requirements(
|
||||
for req_to_add in parse_requirements(
|
||||
filename,
|
||||
finder=finder, options=options, session=session,
|
||||
wheel_cache=wheel_cache):
|
||||
requirement_set.add_requirement(req)
|
||||
req_to_add.is_direct = True
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
# If --require-hashes was a line in a requirements file, tell
|
||||
# RequirementSet about it:
|
||||
requirement_set.require_hashes = options.require_hashes
|
||||
|
|
|
@ -243,10 +243,7 @@ class InstallCommand(RequirementCommand):
|
|||
options.build_dir, delete=build_delete, kind="install"
|
||||
) as directory:
|
||||
requirement_set = RequirementSet(
|
||||
target_dir=target_temp_dir.path,
|
||||
pycompile=options.compile,
|
||||
require_hashes=options.require_hashes,
|
||||
use_user_site=options.use_user_site,
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -300,8 +297,11 @@ class InstallCommand(RequirementCommand):
|
|||
install_options,
|
||||
global_options,
|
||||
root=options.root_path,
|
||||
home=target_temp_dir.path,
|
||||
prefix=options.prefix_path,
|
||||
pycompile=options.compile,
|
||||
warn_script_location=options.warn_script_location,
|
||||
use_user_site=options.use_user_site,
|
||||
)
|
||||
|
||||
possible_lib_locations = get_lib_location_guesses(
|
||||
|
|
|
@ -266,7 +266,7 @@ class RequirementPreparer(object):
|
|||
req.archive(self.download_dir)
|
||||
return abstract_dist
|
||||
|
||||
def prepare_editable_requirement(self, req, require_hashes):
|
||||
def prepare_editable_requirement(self, req, require_hashes, use_user_site):
|
||||
"""Prepare an editable requirement
|
||||
"""
|
||||
assert req.editable, "cannot prepare a non-editable req as editable"
|
||||
|
@ -288,7 +288,7 @@ class RequirementPreparer(object):
|
|||
|
||||
if self._download_should_save:
|
||||
req.archive(self.download_dir)
|
||||
req.check_if_exists()
|
||||
req.check_if_exists(use_user_site)
|
||||
|
||||
return abstract_dist
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class InstallRequirement(object):
|
|||
"""
|
||||
|
||||
def __init__(self, req, comes_from, source_dir=None, editable=False,
|
||||
link=None, update=True, pycompile=True, markers=None,
|
||||
link=None, update=True, markers=None,
|
||||
isolated=False, options=None, wheel_cache=None,
|
||||
constraint=False, extras=()):
|
||||
assert req is None or isinstance(req, Requirement), req
|
||||
|
@ -120,12 +120,10 @@ class InstallRequirement(object):
|
|||
self.install_succeeded = None
|
||||
# UninstallPathSet of uninstalled distribution (for possible rollback)
|
||||
self.uninstalled_pathset = None
|
||||
self.use_user_site = False
|
||||
self.target_dir = None
|
||||
self.options = options if options else {}
|
||||
self.pycompile = pycompile
|
||||
# Set to True after successful preparation of this requirement
|
||||
self.prepared = False
|
||||
self.is_direct = False
|
||||
|
||||
self.isolated = isolated
|
||||
|
||||
|
@ -655,7 +653,8 @@ class InstallRequirement(object):
|
|||
'Unexpected version control type (in %s): %s'
|
||||
% (self.link, vc_type))
|
||||
|
||||
def uninstall(self, auto_confirm=False, verbose=False):
|
||||
def uninstall(self, auto_confirm=False, verbose=False,
|
||||
use_user_site=False):
|
||||
"""
|
||||
Uninstall the distribution currently satisfying this requirement.
|
||||
|
||||
|
@ -668,7 +667,7 @@ class InstallRequirement(object):
|
|||
linked to global site-packages.
|
||||
|
||||
"""
|
||||
if not self.check_if_exists():
|
||||
if not self.check_if_exists(use_user_site):
|
||||
logger.warning("Skipping %s as it is not installed.", self.name)
|
||||
return
|
||||
dist = self.satisfied_by or self.conflicts_with
|
||||
|
@ -746,7 +745,8 @@ class InstallRequirement(object):
|
|||
return True
|
||||
|
||||
def install(self, install_options, global_options=None, root=None,
|
||||
prefix=None, warn_script_location=True):
|
||||
home=None, prefix=None, warn_script_location=True,
|
||||
use_user_site=False, pycompile=True):
|
||||
global_options = global_options if global_options is not None else []
|
||||
if self.editable:
|
||||
self.install_editable(
|
||||
|
@ -758,8 +758,9 @@ class InstallRequirement(object):
|
|||
wheel.check_compatibility(version, self.name)
|
||||
|
||||
self.move_wheel_files(
|
||||
self.source_dir, root=root, prefix=prefix,
|
||||
self.source_dir, root=root, prefix=prefix, home=home,
|
||||
warn_script_location=warn_script_location,
|
||||
use_user_site=use_user_site, pycompile=pycompile,
|
||||
)
|
||||
self.install_succeeded = True
|
||||
return
|
||||
|
@ -778,7 +779,7 @@ class InstallRequirement(object):
|
|||
with TempDirectory(kind="record") as temp_dir:
|
||||
record_filename = os.path.join(temp_dir.path, 'install-record.txt')
|
||||
install_args = self.get_install_args(
|
||||
global_options, record_filename, root, prefix,
|
||||
global_options, record_filename, root, prefix, pycompile,
|
||||
)
|
||||
msg = 'Running setup.py install for %s' % (self.name,)
|
||||
with open_spinner(msg) as spinner:
|
||||
|
@ -845,7 +846,8 @@ class InstallRequirement(object):
|
|||
self.source_dir = self.build_location(parent_dir)
|
||||
return self.source_dir
|
||||
|
||||
def get_install_args(self, global_options, record_filename, root, prefix):
|
||||
def get_install_args(self, global_options, record_filename, root, prefix,
|
||||
pycompile):
|
||||
install_args = [sys.executable, "-u"]
|
||||
install_args.append('-c')
|
||||
install_args.append(SETUPTOOLS_SHIM % self.setup_py)
|
||||
|
@ -858,7 +860,7 @@ class InstallRequirement(object):
|
|||
if prefix is not None:
|
||||
install_args += ['--prefix', prefix]
|
||||
|
||||
if self.pycompile:
|
||||
if pycompile:
|
||||
install_args += ["--compile"]
|
||||
else:
|
||||
install_args += ["--no-compile"]
|
||||
|
@ -910,7 +912,7 @@ class InstallRequirement(object):
|
|||
|
||||
self.install_succeeded = True
|
||||
|
||||
def check_if_exists(self):
|
||||
def check_if_exists(self, use_user_site):
|
||||
"""Find an installed distribution that satisfies or conflicts
|
||||
with this requirement, and set self.satisfied_by or
|
||||
self.conflicts_with appropriately.
|
||||
|
@ -937,7 +939,7 @@ class InstallRequirement(object):
|
|||
existing_dist = pkg_resources.get_distribution(
|
||||
self.req.name
|
||||
)
|
||||
if self.use_user_site:
|
||||
if use_user_site:
|
||||
if dist_in_usersite(existing_dist):
|
||||
self.conflicts_with = existing_dist
|
||||
elif (running_under_virtualenv() and
|
||||
|
@ -955,15 +957,16 @@ class InstallRequirement(object):
|
|||
def is_wheel(self):
|
||||
return self.link and self.link.is_wheel
|
||||
|
||||
def move_wheel_files(self, wheeldir, root=None, prefix=None,
|
||||
warn_script_location=True):
|
||||
def move_wheel_files(self, wheeldir, root=None, home=None, prefix=None,
|
||||
warn_script_location=True, use_user_site=False,
|
||||
pycompile=True):
|
||||
move_wheel_files(
|
||||
self.name, self.req, wheeldir,
|
||||
user=self.use_user_site,
|
||||
home=self.target_dir,
|
||||
user=use_user_site,
|
||||
home=home,
|
||||
root=root,
|
||||
prefix=prefix,
|
||||
pycompile=self.pycompile,
|
||||
pycompile=pycompile,
|
||||
isolated=self.isolated,
|
||||
warn_script_location=warn_script_location,
|
||||
)
|
||||
|
|
|
@ -12,9 +12,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class RequirementSet(object):
|
||||
|
||||
def __init__(self,
|
||||
require_hashes=False, target_dir=None, use_user_site=False,
|
||||
pycompile=True):
|
||||
def __init__(self, require_hashes=False):
|
||||
"""Create a RequirementSet.
|
||||
|
||||
:param wheel_cache: The pip wheel cache, for passing to
|
||||
|
@ -29,9 +27,6 @@ class RequirementSet(object):
|
|||
self.unnamed_requirements = []
|
||||
self.successfully_downloaded = []
|
||||
self.reqs_to_cleanup = []
|
||||
self.use_user_site = use_user_site
|
||||
self.target_dir = target_dir # set from --target option
|
||||
self.pycompile = pycompile
|
||||
|
||||
def __str__(self):
|
||||
reqs = [req for req in self.requirements.values()
|
||||
|
@ -79,10 +74,11 @@ class RequirementSet(object):
|
|||
wheel.filename
|
||||
)
|
||||
|
||||
install_req.use_user_site = self.use_user_site
|
||||
install_req.target_dir = self.target_dir
|
||||
install_req.pycompile = self.pycompile
|
||||
install_req.is_direct = (parent_req_name is None)
|
||||
# This next bit is really a sanity check.
|
||||
assert install_req.is_direct == (parent_req_name is None), (
|
||||
"a direct req shouldn't have a parent and also, "
|
||||
"a non direct req should have a parent"
|
||||
)
|
||||
|
||||
if not name:
|
||||
# url or path requirement w/o an egg fragment
|
||||
|
|
|
@ -150,7 +150,7 @@ class Resolver(object):
|
|||
if self.ignore_installed:
|
||||
return None
|
||||
|
||||
req_to_install.check_if_exists()
|
||||
req_to_install.check_if_exists(self.use_user_site)
|
||||
if not req_to_install.satisfied_by:
|
||||
return None
|
||||
|
||||
|
@ -191,7 +191,7 @@ class Resolver(object):
|
|||
|
||||
if req.editable:
|
||||
return self.preparer.prepare_editable_requirement(
|
||||
req, self.require_hashes
|
||||
req, self.require_hashes, self.use_user_site,
|
||||
)
|
||||
|
||||
# satisfied_by is only evaluated by calling _check_skip_installed,
|
||||
|
@ -219,7 +219,7 @@ class Resolver(object):
|
|||
# pkgs repeat check_if_exists to uninstall-on-upgrade
|
||||
# (#14)
|
||||
if not self.ignore_installed:
|
||||
req.check_if_exists()
|
||||
req.check_if_exists(self.use_user_site)
|
||||
|
||||
if req.satisfied_by:
|
||||
should_modify = (
|
||||
|
@ -291,6 +291,7 @@ class Resolver(object):
|
|||
# can refer to it when adding dependencies.
|
||||
if not requirement_set.has_requirement(req_to_install.name):
|
||||
# 'unnamed' requirements will get added here
|
||||
req_to_install.is_direct = True
|
||||
requirement_set.add_requirement(
|
||||
req_to_install, parent_req_name=None,
|
||||
)
|
||||
|
|
|
@ -24,6 +24,12 @@ from pip._internal.utils.misc import read_text_file
|
|||
from tests.lib import DATA_DIR, assert_raises_regexp, requirements_file
|
||||
|
||||
|
||||
def get_processed_req_from_line(line, fname='file', lineno=1):
|
||||
req = list(process_line(line, fname, lineno))[0]
|
||||
req.is_direct = True
|
||||
return req
|
||||
|
||||
|
||||
class TestRequirementSet(object):
|
||||
"""RequirementSet tests"""
|
||||
|
||||
|
@ -58,6 +64,7 @@ class TestRequirementSet(object):
|
|||
open(os.path.join(build_dir, "setup.py"), 'w')
|
||||
reqset = RequirementSet()
|
||||
req = InstallRequirement.from_line('simple')
|
||||
req.is_direct = True
|
||||
reqset.add_requirement(req)
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
|
@ -76,7 +83,9 @@ class TestRequirementSet(object):
|
|||
"""
|
||||
reqset = RequirementSet()
|
||||
req = InstallRequirement.from_editable(
|
||||
data.packages.join("LocalEnvironMarker"))
|
||||
data.packages.join("LocalEnvironMarker")
|
||||
)
|
||||
req.is_direct = True
|
||||
reqset.add_requirement(req)
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
|
@ -95,28 +104,30 @@ class TestRequirementSet(object):
|
|||
reqset = RequirementSet()
|
||||
# No flags here. This tests that detection of later flags nonetheless
|
||||
# requires earlier packages to have hashes:
|
||||
reqset.add_requirement(
|
||||
list(process_line('blessings==1.0', 'file', 1))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'blessings==1.0', lineno=1
|
||||
))
|
||||
# This flag activates --require-hashes mode:
|
||||
reqset.add_requirement(
|
||||
list(process_line('tracefront==0.1 --hash=sha256:somehash',
|
||||
'file',
|
||||
2))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'tracefront==0.1 --hash=sha256:somehash', lineno=2,
|
||||
))
|
||||
# This hash should be accepted because it came from the reqs file, not
|
||||
# from the internet:
|
||||
reqset.add_requirement(
|
||||
list(process_line('https://pypi.python.org/packages/source/m/more-'
|
||||
'itertools/more-itertools-1.0.tar.gz#md5=b21850c'
|
||||
'3cfa7efbb70fd662ab5413bdd', 'file', 3))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'https://pypi.python.org/packages/source/m/more-itertools/'
|
||||
'more-itertools-1.0.tar.gz#md5=b21850c3cfa7efbb70fd662ab5413bdd',
|
||||
lineno=3,
|
||||
))
|
||||
# The error text should list this as a URL and not `peep==3.1.1`:
|
||||
reqset.add_requirement(
|
||||
list(process_line('https://pypi.python.org/packages/source/p/peep/'
|
||||
'peep-3.1.1.tar.gz',
|
||||
'file',
|
||||
4))[0])
|
||||
finder = PackageFinder([],
|
||||
['https://pypi.python.org/simple'],
|
||||
session=PipSession())
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'https://pypi.python.org/packages/source/p/peep/peep-3.1.1.tar.gz',
|
||||
lineno=4,
|
||||
))
|
||||
finder = PackageFinder(
|
||||
[],
|
||||
['https://pypi.python.org/simple'],
|
||||
session=PipSession(),
|
||||
)
|
||||
resolver = self._basic_resolver(finder)
|
||||
assert_raises_regexp(
|
||||
HashErrors,
|
||||
|
@ -130,17 +141,21 @@ class TestRequirementSet(object):
|
|||
r' Expected sha256 somehash\n'
|
||||
r' Got [0-9a-f]+$',
|
||||
resolver.resolve,
|
||||
reqset)
|
||||
reqset
|
||||
)
|
||||
|
||||
def test_missing_hash_with_require_hashes(self, data):
|
||||
"""Setting --require-hashes explicitly should raise errors if hashes
|
||||
are missing.
|
||||
"""
|
||||
reqset = RequirementSet(require_hashes=True)
|
||||
reqset.add_requirement(
|
||||
list(process_line('simple==1.0', 'file', 1))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'simple==1.0', lineno=1
|
||||
))
|
||||
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
|
||||
assert_raises_regexp(
|
||||
HashErrors,
|
||||
r'Hashes are required in --require-hashes mode, but they are '
|
||||
|
@ -148,7 +163,8 @@ class TestRequirementSet(object):
|
|||
r' simple==1.0 --hash=sha256:393043e672415891885c9a2a0929b1af95'
|
||||
r'fb866d6ca016b42d2e6ce53619b653$',
|
||||
resolver.resolve,
|
||||
reqset)
|
||||
reqset
|
||||
)
|
||||
|
||||
def test_missing_hash_with_require_hashes_in_reqs_file(self, data, tmpdir):
|
||||
"""--require-hashes in a requirements file should make its way to the
|
||||
|
@ -162,7 +178,8 @@ class TestRequirementSet(object):
|
|||
options, args = command.parse_args(['-r', reqs_file])
|
||||
command.populate_requirement_set(
|
||||
req_set, args, options, finder, session, command.name,
|
||||
wheel_cache=None)
|
||||
wheel_cache=None,
|
||||
)
|
||||
assert req_set.require_hashes
|
||||
|
||||
def test_unsupported_hashes(self, data):
|
||||
|
@ -174,17 +191,15 @@ class TestRequirementSet(object):
|
|||
|
||||
"""
|
||||
reqset = RequirementSet(require_hashes=True)
|
||||
reqset.add_requirement(
|
||||
list(process_line(
|
||||
'git+git://github.com/pypa/pip-test-package --hash=sha256:123',
|
||||
'file',
|
||||
1))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'git+git://github.com/pypa/pip-test-package --hash=sha256:123',
|
||||
lineno=1,
|
||||
))
|
||||
dir_path = data.packages.join('FSPkg')
|
||||
reqset.add_requirement(
|
||||
list(process_line(
|
||||
'file://%s' % (dir_path,),
|
||||
'file',
|
||||
2))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'file://%s' % (dir_path,),
|
||||
lineno=2,
|
||||
))
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
sep = os.path.sep
|
||||
|
@ -209,17 +224,16 @@ class TestRequirementSet(object):
|
|||
"""
|
||||
reqset = RequirementSet()
|
||||
# Test that there must be exactly 1 specifier:
|
||||
reqset.add_requirement(
|
||||
list(process_line('simple --hash=sha256:a90427ae31f5d1d0d7ec06ee97'
|
||||
'd9fcf2d0fc9a786985250c1c83fd68df5911dd',
|
||||
'file',
|
||||
1))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'simple --hash=sha256:a90427ae31f5d1d0d7ec06ee97d9fcf2d0fc9a786985'
|
||||
'250c1c83fd68df5911dd', lineno=1,
|
||||
))
|
||||
# Test that the operator must be ==:
|
||||
reqset.add_requirement(list(process_line(
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'simple2>1.0 --hash=sha256:3ad45e1e9aa48b4462af0'
|
||||
'123f6a7e44a9115db1ef945d4d92c123dfe21815a06',
|
||||
'file',
|
||||
2))[0])
|
||||
lineno=2,
|
||||
))
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
assert_raises_regexp(
|
||||
|
@ -236,10 +250,9 @@ class TestRequirementSet(object):
|
|||
file_url = path_to_url(
|
||||
(data.packages / 'simple-1.0.tar.gz').abspath)
|
||||
reqset = RequirementSet(require_hashes=True)
|
||||
reqset.add_requirement(
|
||||
list(process_line('%s --hash=sha256:badbad' % file_url,
|
||||
'file',
|
||||
1))[0])
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'%s --hash=sha256:badbad' % file_url, lineno=1,
|
||||
))
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
assert_raises_regexp(
|
||||
|
@ -258,11 +271,12 @@ class TestRequirementSet(object):
|
|||
reqset = RequirementSet()
|
||||
finder = PackageFinder([data.find_links], [], session=PipSession())
|
||||
resolver = self._basic_resolver(finder)
|
||||
reqset.add_requirement(next(process_line(
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'TopoRequires2==0.0.1 ' # requires TopoRequires
|
||||
'--hash=sha256:eaf9a01242c9f2f42cf2bd82a6a848cd'
|
||||
'e3591d14f7896bdbefcf48543720c970',
|
||||
'file', 1)))
|
||||
lineno=1
|
||||
))
|
||||
assert_raises_regexp(
|
||||
HashErrors,
|
||||
r'In --require-hashes mode, all requirements must have their '
|
||||
|
@ -281,16 +295,18 @@ class TestRequirementSet(object):
|
|||
|
||||
"""
|
||||
reqset = RequirementSet()
|
||||
reqset.add_requirement(next(process_line(
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'TopoRequires2==0.0.1 ' # requires TopoRequires
|
||||
'--hash=sha256:eaf9a01242c9f2f42cf2bd82a6a848cd'
|
||||
'e3591d14f7896bdbefcf48543720c970',
|
||||
'file', 1)))
|
||||
reqset.add_requirement(next(process_line(
|
||||
lineno=1
|
||||
))
|
||||
reqset.add_requirement(get_processed_req_from_line(
|
||||
'TopoRequires==0.0.1 '
|
||||
'--hash=sha256:d6dd1e22e60df512fdcf3640ced3039b3b02a56ab2cee81ebcb'
|
||||
'3d0a6d4e8bfa6',
|
||||
'file', 2)))
|
||||
lineno=2
|
||||
))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('file_contents', 'expected'), [
|
||||
|
@ -590,10 +606,12 @@ def test_exclusive_environment_markers():
|
|||
"""Make sure RequirementSet accepts several excluding env markers"""
|
||||
eq26 = InstallRequirement.from_line(
|
||||
"Django>=1.6.10,<1.7 ; python_version == '2.6'")
|
||||
eq26.is_direct = True
|
||||
ne26 = InstallRequirement.from_line(
|
||||
"Django>=1.6.10,<1.8 ; python_version != '2.6'")
|
||||
ne26.is_direct = True
|
||||
|
||||
req_set = RequirementSet('', '', '')
|
||||
req_set = RequirementSet()
|
||||
req_set.add_requirement(eq26)
|
||||
req_set.add_requirement(ne26)
|
||||
assert req_set.has_requirement('Django')
|
||||
|
|
Loading…
Reference in New Issue