2013-03-27 06:26:52 +01:00
import os
import shutil
2014-01-15 16:57:13 +01:00
import sys
2013-05-01 01:02:10 +02:00
import tempfile
2013-03-27 06:26:52 +01:00
2013-08-18 11:59:44 +02:00
import pytest
2013-11-21 11:06:27 +01:00
from mock import Mock , patch , mock_open
2015-04-08 04:22:37 +02:00
from pip . exceptions import ( PreviousBuildDirError , InvalidWheelFilename ,
UnsupportedWheel )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
from pip . download import path_to_url , PipSession
from pip . exceptions import ( HashMissing , HashUnpinned , VcsHashUnsupported ,
HashErrors , InstallationError )
2013-03-27 06:26:52 +01:00
from pip . index import PackageFinder
2015-04-08 04:22:37 +02:00
from pip . req import ( InstallRequirement , RequirementSet , Requirements )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
from pip . req . req_file import process_line
2015-04-16 00:06:11 +02:00
from pip . req . req_install import parse_editable
2014-08-31 01:52:28 +02:00
from pip . utils import read_text_file
2014-07-04 00:56:26 +02:00
from pip . _vendor import pkg_resources
2013-08-23 13:12:37 +02:00
from tests . lib import assert_raises_regexp
2013-03-27 06:26:52 +01:00
2013-05-01 01:02:10 +02:00
2013-03-27 06:26:52 +01:00
class TestRequirementSet ( object ) :
""" RequirementSet tests """
def setup ( self ) :
self . tempdir = tempfile . mkdtemp ( )
def teardown ( self ) :
shutil . rmtree ( self . tempdir , ignore_errors = True )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
def basic_reqset ( self , * * kwargs ) :
2013-03-27 06:26:52 +01:00
return RequirementSet (
build_dir = os . path . join ( self . tempdir , ' build ' ) ,
src_dir = os . path . join ( self . tempdir , ' src ' ) ,
download_dir = None ,
2014-05-07 01:25:44 +02:00
session = PipSession ( ) ,
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
* * kwargs
2014-02-24 22:52:23 +01:00
)
2013-03-27 06:26:52 +01:00
2013-08-23 13:49:04 +02:00
def test_no_reuse_existing_build_dir ( self , data ) :
2013-03-27 06:26:52 +01:00
""" Test prepare_files raise exception with previous build dir """
build_dir = os . path . join ( self . tempdir , ' build ' , ' simple ' )
os . makedirs ( build_dir )
open ( os . path . join ( build_dir , " setup.py " ) , ' w ' )
reqset = self . basic_reqset ( )
req = InstallRequirement . from_line ( ' simple ' )
reqset . add_requirement ( req )
2014-05-07 01:25:44 +02:00
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
2013-03-27 06:26:52 +01:00
assert_raises_regexp (
PreviousBuildDirError ,
2014-01-28 15:17:51 +01:00
" pip can ' t proceed with [ \ s \ S]* %s [ \ s \ S]* %s " %
( req , build_dir . replace ( ' \\ ' , ' \\ \\ ' ) ) ,
2013-03-27 06:26:52 +01:00
reqset . prepare_files ,
2014-02-24 22:52:23 +01:00
finder ,
)
2013-03-27 06:26:52 +01:00
2014-12-07 23:51:46 +01:00
def test_environment_marker_extras ( self , data ) :
"""
Test that the environment marker extras are used with
non - wheel installs .
"""
reqset = self . basic_reqset ( )
req = InstallRequirement . from_editable (
data . packages . join ( " LocalEnvironMarker " ) )
reqset . add_requirement ( req )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
reqset . prepare_files ( finder )
# This is hacky but does test both case in py2 and py3
if sys . version_info [ : 2 ] in ( ( 2 , 7 ) , ( 3 , 4 ) ) :
assert reqset . has_requirement ( ' simple ' )
else :
assert not reqset . has_requirement ( ' simple ' )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
@pytest.mark.network
def test_missing_hash_checking ( self , data ) :
""" Make sure prepare_files() raises an error when a requirement has no
hash in implicit hash - checking mode .
"""
reqset = self . basic_reqset ( )
# 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 ] )
# This flag activates --require-hashes mode:
reqset . add_requirement (
2015-09-25 00:31:14 +02:00
list ( process_line ( ' tracefront==0.1 --hash=sha256:somehash ' , ' file ' , 2 ) ) [ 0 ] )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
# 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 ] )
finder = PackageFinder ( [ ] ,
[ ' https://pypi.python.org/simple ' ] ,
session = PipSession ( ) )
assert_raises_regexp (
HashErrors ,
r ' These requirements were missing hashes.* \ n '
2015-09-25 00:31:14 +02:00
r ' blessings==1.0 --hash=sha256:[0-9a-f]+ \ n '
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
r ' THESE PACKAGES DID NOT MATCH THE HASHES.* \ n '
r ' tracefront==0.1 .*: \ n '
r ' Expected sha256 somehash \ n '
r ' Got [0-9a-f]+$ ' ,
reqset . prepare_files ,
finder )
def test_missing_hash_with_require_hashes ( self , data ) :
""" Setting --require-hashes explicitly should raise errors if hashes
are missing .
"""
reqset = self . basic_reqset ( require_hashes = True )
reqset . add_requirement (
list ( process_line ( ' simple==1.0 ' , ' file ' , 1 ) ) [ 0 ] )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
assert_raises_regexp (
HashErrors ,
r ' These requirements were missing hashes.* \ n '
2015-09-25 00:31:14 +02:00
r ' simple==1.0 --hash=sha256:393043e672415891885c9a2a0929b1af95fb866 '
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
r ' d6ca016b42d2e6ce53619b653$ ' ,
reqset . prepare_files ,
finder )
def test_unsupported_hashes ( self , data ) : # NEXT: Add any other test cases needed, probably delete the ones in test_install or just have one or two functional tests to make sure prepare_files() gets called when we expect (so we can actually stop on hash errors), clean up, and call it a day. Make sure we test that hashes are checked all 3 places in pip.download. Test http success.
""" VCS and dir links should raise errors when --require-hashes is
on .
In addition , complaints about the type of requirement ( VCS or dir )
should trump the presence or absence of a hash .
"""
reqset = self . basic_reqset ( require_hashes = True )
reqset . add_requirement (
list ( process_line (
2015-09-25 00:31:14 +02:00
' git+git://github.com/pypa/pip-test-package --hash=sha256:12345 ' ,
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
' file ' ,
1 ) ) [ 0 ] )
dir_path = data . packages . join ( ' FSPkg ' )
reqset . add_requirement (
list ( process_line (
' file:// %s ' % ( dir_path , ) ,
' file ' ,
2 ) ) [ 0 ] )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
assert_raises_regexp (
HashErrors ,
r " Can ' t verify hashes for these requirements because we don ' t "
r " have a way to hash version control repositories: \ n "
r " git \ +git://github \ .com/pypa/pip-test-package \ (from -r file "
r " \ (line 1 \ ) \ ) \ n "
r " Can ' t verify hashes for these file:// requirements because they "
r " point to directories: \ n "
r " file:///.*/data/packages/FSPkg \ (from -r file \ (line 2 \ ) \ ) " ,
reqset . prepare_files ,
finder )
def test_unpinned_hash_checking ( self , data ) :
""" Make sure prepare_files() raises an error when a requirement is not
version - pinned in hash - checking mode .
"""
reqset = self . basic_reqset ( )
# Test that there must be exactly 1 specifier:
reqset . add_requirement (
2015-09-25 00:31:14 +02:00
list ( process_line ( ' simple --hash=sha256:a90427ae31f5d1d0d7ec06ee97d9fcf '
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
' 2d0fc9a786985250c1c83fd68df5911dd ' ,
' file ' ,
1 ) ) [ 0 ] )
# Test that the operator must be ==:
reqset . add_requirement (
2015-09-25 00:31:14 +02:00
list ( process_line ( ' simple2>1.0 --hash=sha256:3ad45e1e9aa48b4462af0123f6 '
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
' a7e44a9115db1ef945d4d92c123dfe21815a06 ' ,
' file ' ,
2 ) ) [ 0 ] )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
assert_raises_regexp (
HashErrors ,
# Make sure all failing requirements are listed:
r ' version pinned with ==. These do not: \ n '
r ' simple .* \ (from -r file \ (line 1 \ ) \ ) \ n '
r ' simple2>1.0 .* \ (from -r file \ (line 2 \ ) \ ) ' ,
reqset . prepare_files ,
finder )
def test_hash_mismatch ( self , data ) :
""" A hash mismatch should raise an error. """
file_url = path_to_url (
( data . packages / ' simple-1.0.tar.gz ' ) . abspath )
reqset = self . basic_reqset ( require_hashes = True )
reqset . add_requirement (
2015-09-25 00:31:14 +02:00
list ( process_line ( ' %s --hash=sha256:badbad ' % file_url , ' file ' , 1 ) ) [ 0 ] )
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
assert_raises_regexp (
HashErrors ,
r ' THESE PACKAGES DID NOT MATCH THE HASHES.* \ n '
r ' file:///.*/data/packages/simple-1 \ .0 \ .tar \ .gz .*: \ n '
r ' Expected sha256 badbad \ n '
r ' Got 393043e672415891885c9a2a0929b1af95fb866d '
r ' 6ca016b42d2e6ce53619b653$ ' ,
reqset . prepare_files ,
finder )
def test_no_deps_on_require_hashes ( self , data ) :
""" Make sure --require-hashes mode implies --no-deps. """
reqset = self . basic_reqset ( )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
req = list ( process_line (
' TopoRequires2==0.0.1 '
2015-09-25 00:31:14 +02:00
' --hash=sha256:eaf9a01242c9f2f42cf2bd82a6a848cd '
Add checks against requirements-file-dwelling hashes for most kinds of packages. Close #1175.
* Add --require-hashes option. This is handy in deployment scripts to force application authors to hash their requirements. It is also a convenient way to get pip to show computed hashes for a virgin, unhashed requirements file. Eventually, additions to `pip freeze` should fill a superset of this use case.
* In --require-hashes mode, at least one hash is required to match for each requirement.
* Option-based requirements (--sha256=...) turn on --require-hashes mode implicitly.
* Internet-derived URL-based hashes are "necessary but not sufficient": they do not satisfy --require-hashes mode when they match, but they are still used to guard against transmission errors.
* Other URL-based requirements (#md5=...) are treated just like flag-based ones, except they don't turn on --require-hashes.
* Complain informatively, with the most devastating errors first so you don't chase your tail all day only to run up against a brick wall at the end. This also means we don't complain that a hash is missing, only for the user to find, after fixing it, that we have no idea how to even compute a hash for that type of requirement.
* Complain about unpinned requirements when hash-checking mode is on, lest they cause the user surprise later.
* Complain about missing hashes.
* Complain about requirement types we don't know how to hash (like VCS ones and local dirs).
* Have InstallRequirement keep its original Link around (original_link) so we can differentiate between URL hashes from requirements files and ones downloaded from the (untrustworthy) internet.
* Remove test_download_hashes, which is obsolete. Similar coverage is provided in test_utils.TestHashes and the various hash cases in test_req.py.
2015-09-09 19:01:53 +02:00
' e3591d14f7896bdbefcf48543720c970 ' ,
' file ' , 1 ) ) [ 0 ]
deps = reqset . _prepare_file ( finder , req , require_hashes = True )
assert deps == [ ] , ( ' _prepare_files() resolved dependencies even '
' though --require-hashes was on. ' )
def test_no_egg_on_require_hashes ( self , data ) :
""" Make sure --egg is illegal with --require-hashes.
- - egg would cause dependencies to always be installed , since it cedes
control directly to setuptools .
"""
reqset = self . basic_reqset ( require_hashes = True , as_egg = True )
finder = PackageFinder ( [ data . find_links ] , [ ] , session = PipSession ( ) )
with pytest . raises ( InstallationError ) :
reqset . prepare_files ( finder )
2013-05-28 23:58:08 +02:00
2013-11-21 11:06:27 +01:00
@pytest.mark.parametrize ( ( ' file_contents ' , ' expected ' ) , [
( b ' \xf6 \x80 ' , b ' \xc3 \xb6 \xe2 \x82 \xac ' ) , # cp1252
( b ' \xc3 \xb6 \xe2 \x82 \xac ' , b ' \xc3 \xb6 \xe2 \x82 \xac ' ) , # utf-8
( b ' \xc3 \xb6 \xe2 ' , b ' \xc3 \x83 \xc2 \xb6 \xc3 \xa2 ' ) , # Garbage
] )
def test_egg_info_data ( file_contents , expected ) :
om = mock_open ( read_data = file_contents )
em = Mock ( )
em . return_value = ' cp1252 '
2014-08-31 01:52:28 +02:00
with patch ( ' pip.utils.open ' , om , create = True ) :
2013-11-21 13:28:50 +01:00
with patch ( ' locale.getpreferredencoding ' , em ) :
ret = read_text_file ( ' foo ' )
2013-11-21 11:06:27 +01:00
assert ret == expected . decode ( ' utf-8 ' )
2013-11-15 01:35:24 +01:00
class TestInstallRequirement ( object ) :
2013-05-28 23:58:08 +02:00
2013-11-15 01:35:24 +01:00
def test_url_with_query ( self ) :
""" InstallRequirement should strip the fragment, but not 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 )
2014-12-28 16:50:16 +01:00
assert req . link . url == url + fragment , req . link
2013-11-15 01:35:24 +01:00
def test_unsupported_wheel_requirement_raises ( self ) :
with pytest . raises ( UnsupportedWheel ) :
2014-01-28 15:17:51 +01:00
InstallRequirement . from_line (
' peppercorn-0.4-py2.py3-bogus-any.whl ' ,
)
2013-11-15 01:35:24 +01:00
2014-12-17 02:14:40 +01:00
def test_installed_version_not_installed ( self ) :
req = InstallRequirement . from_line ( ' simple-0.1-py2.py3-none-any.whl ' )
2014-12-23 12:14:35 +01:00
assert req . installed_version is None
2014-12-17 02:14:40 +01:00
2015-03-06 17:56:24 +01:00
def test_str ( self ) :
req = InstallRequirement . from_line ( ' simple==0.1 ' )
assert str ( req ) == ' simple==0.1 '
def test_repr ( self ) :
req = InstallRequirement . from_line ( ' simple==0.1 ' )
2015-03-16 13:44:02 +01:00
assert repr ( req ) == (
' <InstallRequirement object: simple==0.1 editable=False> '
)
2015-03-06 17:56:24 +01:00
2013-11-15 01:35:24 +01:00
def test_invalid_wheel_requirement_raises ( self ) :
with pytest . raises ( InvalidWheelFilename ) :
2014-01-28 15:17:51 +01:00
InstallRequirement . from_line ( ' invalid.whl ' )
2013-05-28 23:58:08 +02:00
2014-05-26 06:00:39 +02:00
def test_wheel_requirement_sets_req_attribute ( self ) :
req = InstallRequirement . from_line ( ' simple-0.1-py2.py3-none-any.whl ' )
assert req . req == pkg_resources . Requirement . parse ( ' simple==0.1 ' )
2014-08-04 06:01:12 +02:00
def test_url_preserved_line_req ( self ) :
""" Confirm the url is preserved in a non-editable requirement """
2014-08-04 06:49:47 +02:00
url = ' git+http://foo.com@ref#egg=foo '
2014-08-04 06:01:12 +02:00
req = InstallRequirement . from_line ( url )
2014-12-28 16:50:16 +01:00
assert req . link . url == url
2014-08-04 06:01:12 +02:00
def test_url_preserved_editable_req ( self ) :
""" Confirm the url is preserved in a editable requirement """
2014-08-04 06:49:47 +02:00
url = ' git+http://foo.com@ref#egg=foo '
2014-08-04 06:01:12 +02:00
req = InstallRequirement . from_editable ( url )
2014-12-28 16:50:16 +01:00
assert req . link . url == url
2014-08-04 06:01:12 +02:00
2015-03-12 20:52:07 +01:00
def test_get_dist ( self ) :
req = InstallRequirement . from_line ( ' foo ' )
req . egg_info_path = Mock ( return_value = ' /path/to/foo.egg-info ' )
dist = req . get_dist ( )
assert isinstance ( dist , pkg_resources . Distribution )
assert dist . project_name == ' foo '
assert dist . location == ' /path/to '
def test_get_dist_trailing_slash ( self ) :
# Tests issue fixed by https://github.com/pypa/pip/pull/2530
req = InstallRequirement . from_line ( ' foo ' )
req . egg_info_path = Mock ( return_value = ' /path/to/foo.egg-info/ ' )
dist = req . get_dist ( )
assert isinstance ( dist , pkg_resources . Distribution )
assert dist . project_name == ' foo '
assert dist . location == ' /path/to '
2014-01-15 16:57:13 +01:00
def test_markers ( self ) :
for line in (
# recommanded syntax
' mock3; python_version >= " 3 " ' ,
# with more spaces
' mock3 ; python_version >= " 3 " ' ,
# without spaces
' mock3;python_version >= " 3 " ' ,
) :
req = InstallRequirement . from_line ( line )
assert req . req . project_name == ' mock3 '
assert req . req . specs == [ ]
assert req . markers == ' python_version >= " 3 " '
def test_markers_semicolon ( self ) :
# check that the markers can contain a semicolon
req = InstallRequirement . from_line ( ' semicolon; os_name == " a; b " ' )
assert req . req . project_name == ' semicolon '
assert req . req . specs == [ ]
assert req . markers == ' os_name == " a; b " '
def test_markers_url ( self ) :
# test "URL; markers" syntax
url = ' http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz '
line = ' %s ; python_version >= " 3 " ' % url
req = InstallRequirement . from_line ( line )
2014-12-28 16:50:16 +01:00
assert req . link . url == url , req . link
2014-01-15 16:57:13 +01:00
assert req . markers == ' python_version >= " 3 " '
# without space, markers are part of the URL
url = ' http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz '
line = ' %s ;python_version >= " 3 " ' % url
req = InstallRequirement . from_line ( line )
2014-12-28 16:50:16 +01:00
assert req . link . url == line , req . link
2014-01-15 16:57:13 +01:00
assert req . markers is None
def test_markers_match ( self ) :
# match
for markers in (
' python_version >= " 1.0 " ' ,
' sys_platform == %r ' % sys . platform ,
) :
line = ' name; ' + markers
req = InstallRequirement . from_line ( line )
assert req . markers == markers
assert req . match_markers ( )
# don't match
for markers in (
' python_version >= " 5.0 " ' ,
' sys_platform != %r ' % sys . platform ,
) :
line = ' name; ' + markers
req = InstallRequirement . from_line ( line )
assert req . markers == markers
assert not req . match_markers ( )
2015-05-31 20:43:39 +02:00
def test_extras_for_line_path_requirement ( self ) :
line = ' SomeProject[ex1,ex2] '
filename = ' filename '
comes_from = ' -r %s (line %s ) ' % ( filename , 1 )
req = InstallRequirement . from_line ( line , comes_from = comes_from )
assert len ( req . extras ) == 2
assert req . extras [ 0 ] == ' ex1 '
assert req . extras [ 1 ] == ' ex2 '
def test_extras_for_line_url_requirement ( self ) :
line = ' git+https://url#egg=SomeProject[ex1,ex2] '
filename = ' filename '
comes_from = ' -r %s (line %s ) ' % ( filename , 1 )
req = InstallRequirement . from_line ( line , comes_from = comes_from )
assert len ( req . extras ) == 2
assert req . extras [ 0 ] == ' ex1 '
assert req . extras [ 1 ] == ' ex2 '
def test_extras_for_editable_path_requirement ( self ) :
url = ' .[ex1,ex2] '
filename = ' filename '
comes_from = ' -r %s (line %s ) ' % ( filename , 1 )
req = InstallRequirement . from_editable ( url , comes_from = comes_from )
assert len ( req . extras ) == 2
assert req . extras [ 0 ] == ' ex1 '
assert req . extras [ 1 ] == ' ex2 '
def test_extras_for_editable_url_requirement ( self ) :
url = ' git+https://url#egg=SomeProject[ex1,ex2] '
filename = ' filename '
comes_from = ' -r %s (line %s ) ' % ( filename , 1 )
req = InstallRequirement . from_editable ( url , comes_from = comes_from )
assert len ( req . extras ) == 2
assert req . extras [ 0 ] == ' ex1 '
assert req . extras [ 1 ] == ' ex2 '
2013-05-28 23:58:08 +02:00
def test_requirements_data_structure_keeps_order ( ) :
requirements = Requirements ( )
requirements [ ' pip ' ] = ' pip '
requirements [ ' nose ' ] = ' nose '
requirements [ ' coverage ' ] = ' coverage '
assert [ ' pip ' , ' nose ' , ' coverage ' ] == list ( requirements . values ( ) )
assert [ ' pip ' , ' nose ' , ' coverage ' ] == list ( requirements . keys ( ) )
def test_requirements_data_structure_implements__repr__ ( ) :
requirements = Requirements ( )
requirements [ ' pip ' ] = ' pip '
requirements [ ' nose ' ] = ' nose '
assert " Requirements( { ' pip ' : ' pip ' , ' nose ' : ' nose ' }) " == repr ( requirements )
def test_requirements_data_structure_implements__contains__ ( ) :
requirements = Requirements ( )
requirements [ ' pip ' ] = ' pip '
assert ' pip ' in requirements
assert ' nose ' not in requirements
2014-01-28 15:17:51 +01:00
2013-05-28 23:58:08 +02:00
@patch ( ' os.path.normcase ' )
2014-01-12 19:05:11 +01:00
@patch ( ' pip.req.req_install.os.getcwd ' )
@patch ( ' pip.req.req_install.os.path.exists ' )
@patch ( ' pip.req.req_install.os.path.isdir ' )
2014-01-28 15:17:51 +01:00
def test_parse_editable_local (
isdir_mock , exists_mock , getcwd_mock , normcase_mock ) :
2013-05-28 23:58:08 +02:00
exists_mock . return_value = isdir_mock . return_value = True
# mocks needed to support path operations on windows tests
normcase_mock . return_value = getcwd_mock . return_value = " /some/path "
2014-12-07 23:49:26 +01:00
assert parse_editable ( ' . ' , ' git ' ) == ( None , ' file:///some/path ' , None , { } )
2013-05-28 23:58:08 +02:00
normcase_mock . return_value = " /some/path/foo "
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' foo ' , ' git ' ) == (
2014-12-07 23:49:26 +01:00
None , ' file:///some/path/foo ' , None , { } ,
2014-01-28 15:17:51 +01:00
)
2013-05-28 23:58:08 +02:00
def test_parse_editable_default_vcs ( ) :
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' https://foo#egg=foo ' , ' git ' ) == (
' foo ' ,
' git+https://foo#egg=foo ' ,
2014-12-07 23:49:26 +01:00
None ,
2014-01-28 15:17:51 +01:00
{ ' egg ' : ' foo ' } ,
)
2013-05-28 23:58:08 +02:00
def test_parse_editable_explicit_vcs ( ) :
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' svn+https://foo#egg=foo ' , ' git ' ) == (
' foo ' ,
' svn+https://foo#egg=foo ' ,
2014-12-07 23:49:26 +01:00
None ,
2014-01-28 15:17:51 +01:00
{ ' egg ' : ' foo ' } ,
)
2013-05-28 23:58:08 +02:00
def test_parse_editable_vcs_extras ( ) :
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' svn+https://foo#egg=foo[extras] ' , ' git ' ) == (
' foo[extras] ' ,
' svn+https://foo#egg=foo[extras] ' ,
2014-12-07 23:49:26 +01:00
None ,
2014-01-28 15:17:51 +01:00
{ ' egg ' : ' foo[extras] ' } ,
)
2013-05-28 23:58:08 +02:00
@patch ( ' os.path.normcase ' )
2014-01-12 19:05:11 +01:00
@patch ( ' pip.req.req_install.os.getcwd ' )
@patch ( ' pip.req.req_install.os.path.exists ' )
@patch ( ' pip.req.req_install.os.path.isdir ' )
2014-01-28 15:17:51 +01:00
def test_parse_editable_local_extras (
isdir_mock , exists_mock , getcwd_mock , normcase_mock ) :
2013-05-28 23:58:08 +02:00
exists_mock . return_value = isdir_mock . return_value = True
normcase_mock . return_value = getcwd_mock . return_value = " /some/path "
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' .[extras] ' , ' git ' ) == (
2014-12-07 23:49:26 +01:00
None , ' file:// ' + " /some/path " , ( ' extras ' , ) , { } ,
2014-01-28 15:17:51 +01:00
)
2013-05-28 23:58:08 +02:00
normcase_mock . return_value = " /some/path/foo "
2014-01-28 15:17:51 +01:00
assert parse_editable ( ' foo[bar,baz] ' , ' git ' ) == (
2014-12-07 23:49:26 +01:00
None , ' file:///some/path/foo ' , ( ' bar ' , ' baz ' ) , { } ,
2014-01-28 15:17:51 +01:00
)
2013-05-28 23:58:08 +02:00
2015-04-07 22:37:38 +02:00
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 ' " )
ne26 = InstallRequirement . from_line (
" Django>=1.6.10,<1.8 ; python_version != ' 2.6 ' " )
req_set = RequirementSet ( ' ' , ' ' , ' ' , session = PipSession ( ) )
req_set . add_requirement ( eq26 )
req_set . add_requirement ( ne26 )
assert req_set . has_requirement ( ' Django ' )