Move InstallRequirement.from_line to constructors module

This commit is contained in:
Pradyun Gedam 2018-08-21 20:37:40 +05:30
parent 69b494aa29
commit a5a07fe61c
No known key found for this signature in database
GPG Key ID: DA17C4B29CB32E4B
11 changed files with 240 additions and 221 deletions

View File

@ -22,9 +22,10 @@ from pip._internal.exceptions import (
) )
from pip._internal.index import PackageFinder from pip._internal.index import PackageFinder
from pip._internal.locations import running_under_virtualenv from pip._internal.locations import running_under_virtualenv
from pip._internal.req.constructors import install_req_from_editable from pip._internal.req.constructors import (
install_req_from_editable, install_req_from_line,
)
from pip._internal.req.req_file import parse_requirements from pip._internal.req.req_file import parse_requirements
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.logging import setup_logging from pip._internal.utils.logging import setup_logging
from pip._internal.utils.misc import get_prog, normalize_path from pip._internal.utils.misc import get_prog, normalize_path
from pip._internal.utils.outdated import pip_version_check from pip._internal.utils.outdated import pip_version_check
@ -209,7 +210,7 @@ class RequirementCommand(Command):
requirement_set.add_requirement(req_to_add) requirement_set.add_requirement(req_to_add)
for req in args: for req in args:
req_to_add = InstallRequirement.from_line( req_to_add = install_req_from_line(
req, None, isolated=options.isolated_mode, req, None, isolated=options.isolated_mode,
wheel_cache=wheel_cache wheel_cache=wheel_cache
) )

View File

@ -4,7 +4,8 @@ from pip._vendor.packaging.utils import canonicalize_name
from pip._internal.cli.base_command import Command from pip._internal.cli.base_command import Command
from pip._internal.exceptions import InstallationError from pip._internal.exceptions import InstallationError
from pip._internal.req import InstallRequirement, parse_requirements from pip._internal.req import parse_requirements
from pip._internal.req.constructors import install_req_from_line
from pip._internal.utils.misc import protect_pip_from_modification_on_windows from pip._internal.utils.misc import protect_pip_from_modification_on_windows
@ -47,7 +48,7 @@ class UninstallCommand(Command):
with self._build_session(options) as session: with self._build_session(options) as session:
reqs_to_uninstall = {} reqs_to_uninstall = {}
for name in args: for name in args:
req = InstallRequirement.from_line( req = install_req_from_line(
name, isolated=options.isolated_mode, name, isolated=options.isolated_mode,
) )
if req.name: if req.name:

View File

@ -10,8 +10,9 @@ from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.pkg_resources import RequirementParseError from pip._vendor.pkg_resources import RequirementParseError
from pip._internal.exceptions import InstallationError from pip._internal.exceptions import InstallationError
from pip._internal.req import InstallRequirement from pip._internal.req.constructors import (
from pip._internal.req.constructors import install_req_from_editable install_req_from_editable, install_req_from_line,
)
from pip._internal.req.req_file import COMMENT_RE from pip._internal.req.req_file import COMMENT_RE
from pip._internal.utils.deprecation import deprecated from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.misc import ( from pip._internal.utils.misc import (
@ -106,7 +107,7 @@ def freeze(
wheel_cache=wheel_cache, wheel_cache=wheel_cache,
) )
else: else:
line_req = InstallRequirement.from_line( line_req = install_req_from_line(
COMMENT_RE.sub('', line).strip(), COMMENT_RE.sub('', line).strip(),
isolated=isolated, isolated=isolated,
wheel_cache=wheel_cache, wheel_cache=wheel_cache,

View File

@ -8,18 +8,45 @@ These are meant to be used elsewhere within pip to create instances of
InstallRequirement. InstallRequirement.
""" """
import logging
import os import os
import re
import traceback
from pip._vendor.packaging.markers import Marker
from pip._vendor.packaging.requirements import InvalidRequirement, Requirement from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
from pip._vendor.packaging.specifiers import Specifier
from pip._vendor.pkg_resources import RequirementParseError, parse_requirements
# XXX: Temporarily importing _strip_extras from pip._internal.download import (
from pip._internal.download import path_to_url, url_to_path is_archive_file, is_url, path_to_url, url_to_path,
)
from pip._internal.exceptions import InstallationError from pip._internal.exceptions import InstallationError
from pip._internal.models.link import Link from pip._internal.models.link import Link
from pip._internal.req.req_install import InstallRequirement, _strip_extras from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.misc import is_installable_dir
from pip._internal.vcs import vcs from pip._internal.vcs import vcs
from pip._internal.wheel import Wheel
__all__ = ["install_req_from_editable", "parse_editable"] __all__ = [
"install_req_from_editable", "install_req_from_line",
"parse_editable"
]
logger = logging.getLogger(__name__)
operators = Specifier._operators.keys()
def _strip_extras(path):
m = re.match(r'^(.+)(\[[^\]]+\])$', path)
extras = None
if m:
path_no_extras = m.group(1)
extras = m.group(2)
else:
path_no_extras = path
return path_no_extras, extras
def parse_editable(editable_req): def parse_editable(editable_req):
@ -87,6 +114,36 @@ def parse_editable(editable_req):
return package_name, url, None return package_name, url, None
def deduce_helpful_msg(req):
"""Returns helpful msg in case requirements file does not exist,
or cannot be parsed.
:params req: Requirements file path
"""
msg = ""
if os.path.exists(req):
msg = " It does exist."
# Try to parse and check if it is a requirements file.
try:
with open(req, 'r') as fp:
# parse first line only
next(parse_requirements(fp.read()))
msg += " The argument you provided " + \
"(%s) appears to be a" % (req) + \
" requirements file. If that is the" + \
" case, use the '-r' flag to install" + \
" the packages specified within it."
except RequirementParseError:
logger.debug("Cannot parse '%s' as requirements \
file" % (req), exc_info=1)
else:
msg += " File '%s' does not exist." % (req)
return msg
# ---- The actual constructors follow ----
def install_req_from_editable( def install_req_from_editable(
editable_req, comes_from=None, isolated=False, options=None, editable_req, comes_from=None, isolated=False, options=None,
wheel_cache=None, constraint=False wheel_cache=None, constraint=False
@ -114,3 +171,102 @@ def install_req_from_editable(
wheel_cache=wheel_cache, wheel_cache=wheel_cache,
extras=extras_override or (), extras=extras_override or (),
) )
def install_req_from_line(
name, comes_from=None, isolated=False, options=None, wheel_cache=None,
constraint=False
):
"""Creates an InstallRequirement from a name, which might be a
requirement, directory containing 'setup.py', filename, or URL.
"""
if is_url(name):
marker_sep = '; '
else:
marker_sep = ';'
if marker_sep in name:
name, markers = name.split(marker_sep, 1)
markers = markers.strip()
if not markers:
markers = None
else:
markers = Marker(markers)
else:
markers = None
name = name.strip()
req = None
path = os.path.normpath(os.path.abspath(name))
link = None
extras = None
if is_url(name):
link = Link(name)
else:
p, extras = _strip_extras(path)
looks_like_dir = os.path.isdir(p) and (
os.path.sep in name or
(os.path.altsep is not None and os.path.altsep in name) or
name.startswith('.')
)
if looks_like_dir:
if not is_installable_dir(p):
raise InstallationError(
"Directory %r is not installable. Neither 'setup.py' "
"nor 'pyproject.toml' found." % name
)
link = Link(path_to_url(p))
elif is_archive_file(p):
if not os.path.isfile(p):
logger.warning(
'Requirement %r looks like a filename, but the '
'file does not exist',
name
)
link = Link(path_to_url(p))
# it's a local file, dir, or url
if link:
# Handle relative file URLs
if link.scheme == 'file' and re.search(r'\.\./', link.url):
link = Link(
path_to_url(os.path.normpath(os.path.abspath(link.path))))
# wheel file
if link.is_wheel:
wheel = Wheel(link.filename) # can raise InvalidWheelFilename
req = "%s==%s" % (wheel.name, wheel.version)
else:
# set the req to the egg fragment. when it's not there, this
# will become an 'unnamed' requirement
req = link.egg_fragment
# a requirement specifier
else:
req = name
if extras:
extras = Requirement("placeholder" + extras.lower()).extras
else:
extras = ()
if req is not None:
try:
req = Requirement(req)
except InvalidRequirement:
if os.path.sep in req:
add_msg = "It looks like a path."
add_msg += deduce_helpful_msg(req)
elif '=' in req and not any(op in req for op in operators):
add_msg = "= is not a valid operator. Did you mean == ?"
else:
add_msg = traceback.format_exc()
raise InstallationError(
"Invalid requirement: '%s'\n%s" % (req, add_msg)
)
return InstallRequirement(
req, comes_from, link=link, markers=markers,
isolated=isolated,
options=options if options else {},
wheel_cache=wheel_cache,
constraint=constraint,
extras=extras,
)

View File

@ -16,8 +16,9 @@ from pip._vendor.six.moves.urllib import parse as urllib_parse
from pip._internal.cli import cmdoptions from pip._internal.cli import cmdoptions
from pip._internal.download import get_file_content from pip._internal.download import get_file_content
from pip._internal.exceptions import RequirementsFileParseError from pip._internal.exceptions import RequirementsFileParseError
from pip._internal.req.constructors import install_req_from_editable from pip._internal.req.constructors import (
from pip._internal.req.req_install import InstallRequirement install_req_from_editable, install_req_from_line,
)
__all__ = ['parse_requirements'] __all__ = ['parse_requirements']
@ -152,7 +153,7 @@ def process_line(line, filename, line_number, finder=None, comes_from=None,
for dest in SUPPORTED_OPTIONS_REQ_DEST: for dest in SUPPORTED_OPTIONS_REQ_DEST:
if dest in opts.__dict__ and opts.__dict__[dest]: if dest in opts.__dict__ and opts.__dict__[dest]:
req_options[dest] = opts.__dict__[dest] req_options[dest] = opts.__dict__[dest]
yield InstallRequirement.from_line( yield install_req_from_line(
args_str, line_comes_from, constraint=constraint, args_str, line_comes_from, constraint=constraint,
isolated=isolated, options=req_options, wheel_cache=wheel_cache isolated=isolated, options=req_options, wheel_cache=wheel_cache
) )

View File

@ -2,27 +2,21 @@ from __future__ import absolute_import
import logging import logging
import os import os
import re
import shutil import shutil
import sys import sys
import sysconfig import sysconfig
import traceback
import zipfile import zipfile
from distutils.util import change_root from distutils.util import change_root
from pip._vendor import pkg_resources, six from pip._vendor import pkg_resources, six
from pip._vendor.packaging import specifiers
from pip._vendor.packaging.markers import Marker
from pip._vendor.packaging.requirements import InvalidRequirement, Requirement from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.version import Version from pip._vendor.packaging.version import Version
from pip._vendor.packaging.version import parse as parse_version from pip._vendor.packaging.version import parse as parse_version
from pip._vendor.pep517.wrappers import Pep517HookCaller from pip._vendor.pep517.wrappers import Pep517HookCaller
from pip._vendor.pkg_resources import RequirementParseError, parse_requirements
from pip._internal import wheel from pip._internal import wheel
from pip._internal.build_env import NoOpBuildEnvironment from pip._internal.build_env import NoOpBuildEnvironment
from pip._internal.download import is_archive_file, is_url, path_to_url
from pip._internal.exceptions import InstallationError from pip._internal.exceptions import InstallationError
from pip._internal.locations import ( from pip._internal.locations import (
PIP_DELETE_MARKER_FILENAME, running_under_virtualenv, PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
@ -37,31 +31,17 @@ from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import ( from pip._internal.utils.misc import (
_make_build_dir, ask_path_exists, backup_dir, call_subprocess, _make_build_dir, ask_path_exists, backup_dir, call_subprocess,
display_path, dist_in_site_packages, dist_in_usersite, ensure_dir, display_path, dist_in_site_packages, dist_in_usersite, ensure_dir,
get_installed_version, is_installable_dir, rmtree, get_installed_version, rmtree,
) )
from pip._internal.utils.packaging import get_metadata from pip._internal.utils.packaging import get_metadata
from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.utils.ui import open_spinner from pip._internal.utils.ui import open_spinner
from pip._internal.vcs import vcs from pip._internal.vcs import vcs
from pip._internal.wheel import Wheel, move_wheel_files from pip._internal.wheel import move_wheel_files
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
operators = specifiers.Specifier._operators.keys()
def _strip_extras(path):
m = re.match(r'^(.+)(\[[^\]]+\])$', path)
extras = None
if m:
path_no_extras = m.group(1)
extras = m.group(2)
else:
path_no_extras = path
return path_no_extras, extras
class InstallRequirement(object): class InstallRequirement(object):
""" """
@ -168,102 +148,6 @@ class InstallRequirement(object):
return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache) return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache)
@classmethod
def from_line(
cls, name, comes_from=None, isolated=False, options=None,
wheel_cache=None, constraint=False):
"""Creates an InstallRequirement from a name, which might be a
requirement, directory containing 'setup.py', filename, or URL.
"""
if is_url(name):
marker_sep = '; '
else:
marker_sep = ';'
if marker_sep in name:
name, markers = name.split(marker_sep, 1)
markers = markers.strip()
if not markers:
markers = None
else:
markers = Marker(markers)
else:
markers = None
name = name.strip()
req = None
path = os.path.normpath(os.path.abspath(name))
link = None
extras = None
if is_url(name):
link = Link(name)
else:
p, extras = _strip_extras(path)
looks_like_dir = os.path.isdir(p) and (
os.path.sep in name or
(os.path.altsep is not None and os.path.altsep in name) or
name.startswith('.')
)
if looks_like_dir:
if not is_installable_dir(p):
raise InstallationError(
"Directory %r is not installable. Neither 'setup.py' "
"nor 'pyproject.toml' found." % name
)
link = Link(path_to_url(p))
elif is_archive_file(p):
if not os.path.isfile(p):
logger.warning(
'Requirement %r looks like a filename, but the '
'file does not exist',
name
)
link = Link(path_to_url(p))
# it's a local file, dir, or url
if link:
# Handle relative file URLs
if link.scheme == 'file' and re.search(r'\.\./', link.url):
link = Link(
path_to_url(os.path.normpath(os.path.abspath(link.path))))
# wheel file
if link.is_wheel:
wheel = Wheel(link.filename) # can raise InvalidWheelFilename
req = "%s==%s" % (wheel.name, wheel.version)
else:
# set the req to the egg fragment. when it's not there, this
# will become an 'unnamed' requirement
req = link.egg_fragment
# a requirement specifier
else:
req = name
if extras:
extras = Requirement("placeholder" + extras.lower()).extras
else:
extras = ()
if req is not None:
try:
req = Requirement(req)
except InvalidRequirement:
if os.path.sep in req:
add_msg = "It looks like a path."
add_msg += deduce_helpful_msg(req)
elif '=' in req and not any(op in req for op in operators):
add_msg = "= is not a valid operator. Did you mean == ?"
else:
add_msg = traceback.format_exc()
raise InstallationError(
"Invalid requirement: '%s'\n%s" % (req, add_msg))
return cls(
req, comes_from, link=link, markers=markers,
isolated=isolated,
options=options if options else {},
wheel_cache=wheel_cache,
constraint=constraint,
extras=extras,
)
def __str__(self): def __str__(self):
if self.req: if self.req:
s = str(self.req) s = str(self.req)
@ -998,30 +882,3 @@ class InstallRequirement(object):
py_ver_str, self.name)] py_ver_str, self.name)]
return install_args return install_args
def deduce_helpful_msg(req):
"""Returns helpful msg in case requirements file does not exist,
or cannot be parsed.
:params req: Requirements file path
"""
msg = ""
if os.path.exists(req):
msg = " It does exist."
# Try to parse and check if it is a requirements file.
try:
with open(req, 'r') as fp:
# parse first line only
next(parse_requirements(fp.read()))
msg += " The argument you provided " + \
"(%s) appears to be a" % (req) + \
" requirements file. If that is the" + \
" case, use the '-r' flag to install" + \
" the packages specified within it."
except RequirementParseError:
logger.debug("Cannot parse '%s' as requirements \
file" % (req), exc_info=1)
else:
msg += " File '%s' does not exist." % (req)
return msg

View File

@ -11,7 +11,7 @@ from tempfile import mkdtemp
import pretend import pretend
import pytest import pytest
from pip._internal.req import InstallRequirement from pip._internal.req.constructors import install_req_from_line
from pip._internal.utils.misc import rmtree from pip._internal.utils.misc import rmtree
from tests.lib import assert_all_changes, create_test_package_with_setup from tests.lib import assert_all_changes, create_test_package_with_setup
from tests.lib.local_repos import local_checkout, local_repo from tests.lib.local_repos import local_checkout, local_repo
@ -439,7 +439,7 @@ def test_uninstall_non_local_distutils(caplog, monkeypatch, tmpdir):
get_dist = pretend.call_recorder(lambda x: dist) get_dist = pretend.call_recorder(lambda x: dist)
monkeypatch.setattr("pip._vendor.pkg_resources.get_distribution", get_dist) monkeypatch.setattr("pip._vendor.pkg_resources.get_distribution", get_dist)
req = InstallRequirement.from_line("thing") req = install_req_from_line("thing")
req.uninstall() req.uninstall()
assert os.path.exists(einfo) assert os.path.exists(einfo)

View File

@ -14,13 +14,13 @@ from pip._internal.exceptions import (
from pip._internal.index import ( from pip._internal.index import (
FormatControl, InstallationCandidate, Link, PackageFinder, fmt_ctl_formats, FormatControl, InstallationCandidate, Link, PackageFinder, fmt_ctl_formats,
) )
from pip._internal.req import InstallRequirement from pip._internal.req.constructors import install_req_from_line
def test_no_mpkg(data): def test_no_mpkg(data):
"""Finder skips zipfiles with "macosx10" in the name.""" """Finder skips zipfiles with "macosx10" in the name."""
finder = PackageFinder([data.find_links], [], session=PipSession()) finder = PackageFinder([data.find_links], [], session=PipSession())
req = InstallRequirement.from_line("pkgwithmpkg") req = install_req_from_line("pkgwithmpkg")
found = finder.find_requirement(req, False) found = finder.find_requirement(req, False)
assert found.url.endswith("pkgwithmpkg-1.0.tar.gz"), found assert found.url.endswith("pkgwithmpkg-1.0.tar.gz"), found
@ -29,7 +29,7 @@ def test_no_mpkg(data):
def test_no_partial_name_match(data): def test_no_partial_name_match(data):
"""Finder requires the full project name to match, not just beginning.""" """Finder requires the full project name to match, not just beginning."""
finder = PackageFinder([data.find_links], [], session=PipSession()) finder = PackageFinder([data.find_links], [], session=PipSession())
req = InstallRequirement.from_line("gmpy") req = install_req_from_line("gmpy")
found = finder.find_requirement(req, False) found = finder.find_requirement(req, False)
assert found.url.endswith("gmpy-1.15.tar.gz"), found assert found.url.endswith("gmpy-1.15.tar.gz"), found
@ -40,7 +40,7 @@ def test_tilde():
session = PipSession() session = PipSession()
with patch('pip._internal.index.os.path.exists', return_value=True): with patch('pip._internal.index.os.path.exists', return_value=True):
finder = PackageFinder(['~/python-pkgs'], [], session=session) finder = PackageFinder(['~/python-pkgs'], [], session=session)
req = InstallRequirement.from_line("gmpy") req = install_req_from_line("gmpy")
with pytest.raises(DistributionNotFound): with pytest.raises(DistributionNotFound):
finder.find_requirement(req, False) finder.find_requirement(req, False)
@ -53,7 +53,7 @@ def test_duplicates_sort_ok(data):
[], [],
session=PipSession(), session=PipSession(),
) )
req = InstallRequirement.from_line("duplicate") req = install_req_from_line("duplicate")
found = finder.find_requirement(req, False) found = finder.find_requirement(req, False)
assert found.url.endswith("duplicate-1.0.tar.gz"), found assert found.url.endswith("duplicate-1.0.tar.gz"), found
@ -61,7 +61,7 @@ def test_duplicates_sort_ok(data):
def test_finder_detects_latest_find_links(data): def test_finder_detects_latest_find_links(data):
"""Test PackageFinder detects latest using find-links""" """Test PackageFinder detects latest using find-links"""
req = InstallRequirement.from_line('simple', None) req = install_req_from_line('simple', None)
finder = PackageFinder([data.find_links], [], session=PipSession()) finder = PackageFinder([data.find_links], [], session=PipSession())
link = finder.find_requirement(req, False) link = finder.find_requirement(req, False)
assert link.url.endswith("simple-3.0.tar.gz") assert link.url.endswith("simple-3.0.tar.gz")
@ -69,7 +69,7 @@ def test_finder_detects_latest_find_links(data):
def test_incorrect_case_file_index(data): def test_incorrect_case_file_index(data):
"""Test PackageFinder detects latest using wrong case""" """Test PackageFinder detects latest using wrong case"""
req = InstallRequirement.from_line('dinner', None) req = install_req_from_line('dinner', None)
finder = PackageFinder([], [data.find_links3], session=PipSession()) finder = PackageFinder([], [data.find_links3], session=PipSession())
link = finder.find_requirement(req, False) link = finder.find_requirement(req, False)
assert link.url.endswith("Dinner-2.0.tar.gz") assert link.url.endswith("Dinner-2.0.tar.gz")
@ -78,7 +78,7 @@ def test_incorrect_case_file_index(data):
@pytest.mark.network @pytest.mark.network
def test_finder_detects_latest_already_satisfied_find_links(data): def test_finder_detects_latest_already_satisfied_find_links(data):
"""Test PackageFinder detects latest already satisfied using find-links""" """Test PackageFinder detects latest already satisfied using find-links"""
req = InstallRequirement.from_line('simple', None) req = install_req_from_line('simple', None)
# the latest simple in local pkgs is 3.0 # the latest simple in local pkgs is 3.0
latest_version = "3.0" latest_version = "3.0"
satisfied_by = Mock( satisfied_by = Mock(
@ -96,7 +96,7 @@ def test_finder_detects_latest_already_satisfied_find_links(data):
@pytest.mark.network @pytest.mark.network
def test_finder_detects_latest_already_satisfied_pypi_links(): def test_finder_detects_latest_already_satisfied_pypi_links():
"""Test PackageFinder detects latest already satisfied using pypi links""" """Test PackageFinder detects latest already satisfied using pypi links"""
req = InstallRequirement.from_line('initools', None) req = install_req_from_line('initools', None)
# the latest initools on pypi is 0.3.1 # the latest initools on pypi is 0.3.1
latest_version = "0.3.1" latest_version = "0.3.1"
satisfied_by = Mock( satisfied_by = Mock(
@ -123,7 +123,7 @@ class TestWheel:
""" """
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
req = InstallRequirement.from_line("invalid") req = install_req_from_line("invalid")
# data.find_links contains "invalid.whl", which is an invalid wheel # data.find_links contains "invalid.whl", which is an invalid wheel
finder = PackageFinder( finder = PackageFinder(
[data.find_links], [data.find_links],
@ -148,7 +148,7 @@ class TestWheel:
lambda **kw: [("py1", "none", "any")], lambda **kw: [("py1", "none", "any")],
) )
req = InstallRequirement.from_line("simple.dist") req = install_req_from_line("simple.dist")
finder = PackageFinder( finder = PackageFinder(
[data.find_links], [data.find_links],
[], [],
@ -169,7 +169,7 @@ class TestWheel:
lambda **kw: [('py2', 'none', 'any')], lambda **kw: [('py2', 'none', 'any')],
) )
req = InstallRequirement.from_line("simple.dist") req = install_req_from_line("simple.dist")
finder = PackageFinder( finder = PackageFinder(
[data.find_links], [data.find_links],
[], [],
@ -185,7 +185,7 @@ class TestWheel:
Test wheels have priority over sdists. Test wheels have priority over sdists.
`test_link_sorting` also covers this at lower level `test_link_sorting` also covers this at lower level
""" """
req = InstallRequirement.from_line("priority") req = install_req_from_line("priority")
finder = PackageFinder( finder = PackageFinder(
[data.find_links], [data.find_links],
[], [],
@ -199,7 +199,7 @@ class TestWheel:
Test existing install has priority over wheels. Test existing install has priority over wheels.
`test_link_sorting` also covers this at a lower level `test_link_sorting` also covers this at a lower level
""" """
req = InstallRequirement.from_line('priority', None) req = install_req_from_line('priority', None)
latest_version = "1.0" latest_version = "1.0"
satisfied_by = Mock( satisfied_by = Mock(
location="/path", location="/path",
@ -284,7 +284,7 @@ class TestWheel:
def test_finder_priority_file_over_page(data): def test_finder_priority_file_over_page(data):
"""Test PackageFinder prefers file links over equivalent page links""" """Test PackageFinder prefers file links over equivalent page links"""
req = InstallRequirement.from_line('gmpy==1.15', None) req = install_req_from_line('gmpy==1.15', None)
finder = PackageFinder( finder = PackageFinder(
[data.find_links], [data.find_links],
["http://pypi.org/simple/"], ["http://pypi.org/simple/"],
@ -304,7 +304,7 @@ def test_finder_deplink():
""" """
Test PackageFinder with dependency links only Test PackageFinder with dependency links only
""" """
req = InstallRequirement.from_line('gmpy==1.15', None) req = install_req_from_line('gmpy==1.15', None)
finder = PackageFinder( finder = PackageFinder(
[], [],
[], [],
@ -323,7 +323,7 @@ def test_finder_priority_page_over_deplink():
""" """
Test PackageFinder prefers page links over equivalent dependency links Test PackageFinder prefers page links over equivalent dependency links
""" """
req = InstallRequirement.from_line('pip==1.5.6', None) req = install_req_from_line('pip==1.5.6', None)
finder = PackageFinder( finder = PackageFinder(
[], [],
["https://pypi.org/simple/"], ["https://pypi.org/simple/"],
@ -346,7 +346,7 @@ def test_finder_priority_page_over_deplink():
def test_finder_priority_nonegg_over_eggfragments(): def test_finder_priority_nonegg_over_eggfragments():
"""Test PackageFinder prefers non-egg links over "#egg=" links""" """Test PackageFinder prefers non-egg links over "#egg=" links"""
req = InstallRequirement.from_line('bar==1.0', None) req = install_req_from_line('bar==1.0', None)
links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz'] links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz']
finder = PackageFinder(links, [], session=PipSession()) finder = PackageFinder(links, [], session=PipSession())
@ -377,7 +377,7 @@ def test_finder_only_installs_stable_releases(data):
Test PackageFinder only accepts stable versioned releases by default. Test PackageFinder only accepts stable versioned releases by default.
""" """
req = InstallRequirement.from_line("bar", None) req = install_req_from_line("bar", None)
# using a local index (that has pre & dev releases) # using a local index (that has pre & dev releases)
finder = PackageFinder([], [data.index_url("pre")], session=PipSession()) finder = PackageFinder([], [data.index_url("pre")], session=PipSession())
@ -431,7 +431,7 @@ def test_finder_installs_pre_releases(data):
Test PackageFinder finds pre-releases if asked to. Test PackageFinder finds pre-releases if asked to.
""" """
req = InstallRequirement.from_line("bar", None) req = install_req_from_line("bar", None)
# using a local index (that has pre & dev releases) # using a local index (that has pre & dev releases)
finder = PackageFinder( finder = PackageFinder(
@ -471,7 +471,7 @@ def test_finder_installs_dev_releases(data):
Test PackageFinder finds dev releases if asked to. Test PackageFinder finds dev releases if asked to.
""" """
req = InstallRequirement.from_line("bar", None) req = install_req_from_line("bar", None)
# using a local index (that has dev releases) # using a local index (that has dev releases)
finder = PackageFinder( finder = PackageFinder(
@ -487,7 +487,7 @@ def test_finder_installs_pre_releases_with_version_spec():
""" """
Test PackageFinder only accepts stable versioned releases by default. Test PackageFinder only accepts stable versioned releases by default.
""" """
req = InstallRequirement.from_line("bar>=0.0.dev0", None) req = install_req_from_line("bar>=0.0.dev0", None)
links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"]
finder = PackageFinder(links, [], session=PipSession()) finder = PackageFinder(links, [], session=PipSession())
@ -555,7 +555,7 @@ def test_get_index_urls_locations():
finder = PackageFinder( finder = PackageFinder(
[], ['file://index1/', 'file://index2'], session=PipSession()) [], ['file://index1/', 'file://index2'], session=PipSession())
locations = finder._get_index_urls_locations( locations = finder._get_index_urls_locations(
InstallRequirement.from_line('Complex_Name').name) install_req_from_line('Complex_Name').name)
assert locations == ['file://index1/complex-name/', assert locations == ['file://index1/complex-name/',
'file://index2/complex-name/'] 'file://index2/complex-name/']

View File

@ -18,7 +18,7 @@ from pip._internal.index import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req import InstallRequirement, RequirementSet from pip._internal.req import InstallRequirement, RequirementSet
from pip._internal.req.constructors import ( from pip._internal.req.constructors import (
install_req_from_editable, parse_editable, install_req_from_editable, install_req_from_line, parse_editable,
) )
from pip._internal.req.req_file import process_line from pip._internal.req.req_file import process_line
from pip._internal.req.req_tracker import RequirementTracker from pip._internal.req.req_tracker import RequirementTracker
@ -68,7 +68,7 @@ class TestRequirementSet(object):
os.makedirs(build_dir) os.makedirs(build_dir)
open(os.path.join(build_dir, "setup.py"), 'w') open(os.path.join(build_dir, "setup.py"), 'w')
reqset = RequirementSet() reqset = RequirementSet()
req = InstallRequirement.from_line('simple') req = install_req_from_line('simple')
req.is_direct = True req.is_direct = True
reqset.add_requirement(req) reqset.add_requirement(req)
finder = PackageFinder([data.find_links], [], session=PipSession()) finder = PackageFinder([data.find_links], [], session=PipSession())
@ -341,12 +341,12 @@ class TestInstallRequirement(object):
"""InstallRequirement should strip the fragment, but not the query.""" """InstallRequirement should strip the fragment, but not the query."""
url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz'
fragment = '#egg=bar' fragment = '#egg=bar'
req = InstallRequirement.from_line(url + fragment) req = install_req_from_line(url + fragment)
assert req.link.url == url + fragment, req.link assert req.link.url == url + fragment, req.link
def test_unsupported_wheel_link_requirement_raises(self): def test_unsupported_wheel_link_requirement_raises(self):
reqset = RequirementSet() reqset = RequirementSet()
req = InstallRequirement.from_line( req = install_req_from_line(
'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl', 'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl',
) )
assert req.link is not None assert req.link is not None
@ -358,7 +358,7 @@ class TestInstallRequirement(object):
def test_unsupported_wheel_local_file_requirement_raises(self, data): def test_unsupported_wheel_local_file_requirement_raises(self, data):
reqset = RequirementSet() reqset = RequirementSet()
req = InstallRequirement.from_line( req = install_req_from_line(
data.packages.join('simple.dist-0.1-py1-none-invalid.whl'), data.packages.join('simple.dist-0.1-py1-none-invalid.whl'),
) )
assert req.link is not None assert req.link is not None
@ -369,32 +369,32 @@ class TestInstallRequirement(object):
reqset.add_requirement(req) reqset.add_requirement(req)
def test_installed_version_not_installed(self): def test_installed_version_not_installed(self):
req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') req = install_req_from_line('simple-0.1-py2.py3-none-any.whl')
assert req.installed_version is None assert req.installed_version is None
def test_str(self): def test_str(self):
req = InstallRequirement.from_line('simple==0.1') req = install_req_from_line('simple==0.1')
assert str(req) == 'simple==0.1' assert str(req) == 'simple==0.1'
def test_repr(self): def test_repr(self):
req = InstallRequirement.from_line('simple==0.1') req = install_req_from_line('simple==0.1')
assert repr(req) == ( assert repr(req) == (
'<InstallRequirement object: simple==0.1 editable=False>' '<InstallRequirement object: simple==0.1 editable=False>'
) )
def test_invalid_wheel_requirement_raises(self): def test_invalid_wheel_requirement_raises(self):
with pytest.raises(InvalidWheelFilename): with pytest.raises(InvalidWheelFilename):
InstallRequirement.from_line('invalid.whl') install_req_from_line('invalid.whl')
def test_wheel_requirement_sets_req_attribute(self): def test_wheel_requirement_sets_req_attribute(self):
req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') req = install_req_from_line('simple-0.1-py2.py3-none-any.whl')
assert isinstance(req.req, Requirement) assert isinstance(req.req, Requirement)
assert str(req.req) == 'simple==0.1' assert str(req.req) == 'simple==0.1'
def test_url_preserved_line_req(self): def test_url_preserved_line_req(self):
"""Confirm the url is preserved in a non-editable requirement""" """Confirm the url is preserved in a non-editable requirement"""
url = 'git+http://foo.com@ref#egg=foo' url = 'git+http://foo.com@ref#egg=foo'
req = InstallRequirement.from_line(url) req = install_req_from_line(url)
assert req.link.url == url assert req.link.url == url
def test_url_preserved_editable_req(self): def test_url_preserved_editable_req(self):
@ -409,7 +409,7 @@ class TestInstallRequirement(object):
'/path/to/foo.egg-info/'.replace('/', os.path.sep), '/path/to/foo.egg-info/'.replace('/', os.path.sep),
)) ))
def test_get_dist(self, path): def test_get_dist(self, path):
req = InstallRequirement.from_line('foo') req = install_req_from_line('foo')
req._egg_info_path = path req._egg_info_path = path
dist = req.get_dist() dist = req.get_dist()
assert isinstance(dist, pkg_resources.Distribution) assert isinstance(dist, pkg_resources.Distribution)
@ -425,14 +425,14 @@ class TestInstallRequirement(object):
# without spaces # without spaces
'mock3;python_version >= "3"', 'mock3;python_version >= "3"',
): ):
req = InstallRequirement.from_line(line) req = install_req_from_line(line)
assert req.req.name == 'mock3' assert req.req.name == 'mock3'
assert str(req.req.specifier) == '' assert str(req.req.specifier) == ''
assert str(req.markers) == 'python_version >= "3"' assert str(req.markers) == 'python_version >= "3"'
def test_markers_semicolon(self): def test_markers_semicolon(self):
# check that the markers can contain a semicolon # check that the markers can contain a semicolon
req = InstallRequirement.from_line('semicolon; os_name == "a; b"') req = install_req_from_line('semicolon; os_name == "a; b"')
assert req.req.name == 'semicolon' assert req.req.name == 'semicolon'
assert str(req.req.specifier) == '' assert str(req.req.specifier) == ''
assert str(req.markers) == 'os_name == "a; b"' assert str(req.markers) == 'os_name == "a; b"'
@ -441,14 +441,14 @@ class TestInstallRequirement(object):
# test "URL; markers" syntax # test "URL; markers" syntax
url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz'
line = '%s; python_version >= "3"' % url line = '%s; python_version >= "3"' % url
req = InstallRequirement.from_line(line) req = install_req_from_line(line)
assert req.link.url == url, req.url assert req.link.url == url, req.url
assert str(req.markers) == 'python_version >= "3"' assert str(req.markers) == 'python_version >= "3"'
# without space, markers are part of the URL # without space, markers are part of the URL
url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz'
line = '%s;python_version >= "3"' % url line = '%s;python_version >= "3"' % url
req = InstallRequirement.from_line(line) req = install_req_from_line(line)
assert req.link.url == line, req.url assert req.link.url == line, req.url
assert req.markers is None assert req.markers is None
@ -459,7 +459,7 @@ class TestInstallRequirement(object):
'sys_platform == %r' % sys.platform, 'sys_platform == %r' % sys.platform,
): ):
line = 'name; ' + markers line = 'name; ' + markers
req = InstallRequirement.from_line(line) req = install_req_from_line(line)
assert str(req.markers) == str(Marker(markers)) assert str(req.markers) == str(Marker(markers))
assert req.match_markers() assert req.match_markers()
@ -469,7 +469,7 @@ class TestInstallRequirement(object):
'sys_platform != %r' % sys.platform, 'sys_platform != %r' % sys.platform,
): ):
line = 'name; ' + markers line = 'name; ' + markers
req = InstallRequirement.from_line(line) req = install_req_from_line(line)
assert str(req.markers) == str(Marker(markers)) assert str(req.markers) == str(Marker(markers))
assert not req.match_markers() assert not req.match_markers()
@ -480,7 +480,7 @@ class TestInstallRequirement(object):
'sys_platform == %r' % sys.platform, 'sys_platform == %r' % sys.platform,
): ):
line = 'name; ' + markers line = 'name; ' + markers
req = InstallRequirement.from_line(line, comes_from='') req = install_req_from_line(line, comes_from='')
assert str(req.markers) == str(Marker(markers)) assert str(req.markers) == str(Marker(markers))
assert req.match_markers() assert req.match_markers()
@ -490,7 +490,7 @@ class TestInstallRequirement(object):
'sys_platform != %r' % sys.platform, 'sys_platform != %r' % sys.platform,
): ):
line = 'name; ' + markers line = 'name; ' + markers
req = InstallRequirement.from_line(line, comes_from='') req = install_req_from_line(line, comes_from='')
assert str(req.markers) == str(Marker(markers)) assert str(req.markers) == str(Marker(markers))
assert not req.match_markers() assert not req.match_markers()
@ -498,7 +498,7 @@ class TestInstallRequirement(object):
line = 'SomeProject[ex1,ex2]' line = 'SomeProject[ex1,ex2]'
filename = 'filename' filename = 'filename'
comes_from = '-r %s (line %s)' % (filename, 1) comes_from = '-r %s (line %s)' % (filename, 1)
req = InstallRequirement.from_line(line, comes_from=comes_from) req = install_req_from_line(line, comes_from=comes_from)
assert len(req.extras) == 2 assert len(req.extras) == 2
assert req.extras == {'ex1', 'ex2'} assert req.extras == {'ex1', 'ex2'}
@ -506,7 +506,7 @@ class TestInstallRequirement(object):
line = 'git+https://url#egg=SomeProject[ex1,ex2]' line = 'git+https://url#egg=SomeProject[ex1,ex2]'
filename = 'filename' filename = 'filename'
comes_from = '-r %s (line %s)' % (filename, 1) comes_from = '-r %s (line %s)' % (filename, 1)
req = InstallRequirement.from_line(line, comes_from=comes_from) req = install_req_from_line(line, comes_from=comes_from)
assert len(req.extras) == 2 assert len(req.extras) == 2
assert req.extras == {'ex1', 'ex2'} assert req.extras == {'ex1', 'ex2'}
@ -528,7 +528,7 @@ class TestInstallRequirement(object):
def test_unexisting_path(self): def test_unexisting_path(self):
with pytest.raises(InstallationError) as e: with pytest.raises(InstallationError) as e:
InstallRequirement.from_line( install_req_from_line(
os.path.join('this', 'path', 'does', 'not', 'exist')) os.path.join('this', 'path', 'does', 'not', 'exist'))
err_msg = e.value.args[0] err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg assert "Invalid requirement" in err_msg
@ -536,14 +536,14 @@ class TestInstallRequirement(object):
def test_single_equal_sign(self): def test_single_equal_sign(self):
with pytest.raises(InstallationError) as e: with pytest.raises(InstallationError) as e:
InstallRequirement.from_line('toto=42') install_req_from_line('toto=42')
err_msg = e.value.args[0] err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg assert "Invalid requirement" in err_msg
assert "= is not a valid operator. Did you mean == ?" in err_msg assert "= is not a valid operator. Did you mean == ?" in err_msg
def test_traceback(self): def test_traceback(self):
with pytest.raises(InstallationError) as e: with pytest.raises(InstallationError) as e:
InstallRequirement.from_line('toto 42') install_req_from_line('toto 42')
err_msg = e.value.args[0] err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg assert "Invalid requirement" in err_msg
assert "\nTraceback " in err_msg assert "\nTraceback " in err_msg
@ -553,7 +553,7 @@ class TestInstallRequirement(object):
with open(req_file_path, 'w') as req_file: with open(req_file_path, 'w') as req_file:
req_file.write('pip\nsetuptools') req_file.write('pip\nsetuptools')
with pytest.raises(InstallationError) as e: with pytest.raises(InstallationError) as e:
InstallRequirement.from_line(req_file_path) install_req_from_line(req_file_path)
err_msg = e.value.args[0] err_msg = e.value.args[0]
assert "Invalid requirement" in err_msg assert "Invalid requirement" in err_msg
assert "It looks like a path. It does exist." in err_msg assert "It looks like a path. It does exist." in err_msg
@ -610,10 +610,10 @@ def test_parse_editable_local_extras(
def test_exclusive_environment_markers(): def test_exclusive_environment_markers():
"""Make sure RequirementSet accepts several excluding env markers""" """Make sure RequirementSet accepts several excluding env markers"""
eq26 = InstallRequirement.from_line( eq26 = install_req_from_line(
"Django>=1.6.10,<1.7 ; python_version == '2.6'") "Django>=1.6.10,<1.7 ; python_version == '2.6'")
eq26.is_direct = True eq26.is_direct = True
ne26 = InstallRequirement.from_line( ne26 = install_req_from_line(
"Django>=1.6.10,<1.8 ; python_version != '2.6'") "Django>=1.6.10,<1.8 ; python_version != '2.6'")
ne26.is_direct = True ne26.is_direct = True

View File

@ -12,12 +12,13 @@ from pip._internal.exceptions import (
InstallationError, RequirementsFileParseError, InstallationError, RequirementsFileParseError,
) )
from pip._internal.index import PackageFinder from pip._internal.index import PackageFinder
from pip._internal.req.constructors import install_req_from_editable from pip._internal.req.constructors import (
install_req_from_editable, install_req_from_line,
)
from pip._internal.req.req_file import ( from pip._internal.req.req_file import (
break_args_options, ignore_comments, join_lines, parse_requirements, break_args_options, ignore_comments, join_lines, parse_requirements,
preprocess, process_line, skip_regex, preprocess, process_line, skip_regex,
) )
from pip._internal.req.req_install import InstallRequirement
from tests.lib import requirements_file from tests.lib import requirements_file
@ -193,14 +194,14 @@ class TestProcessLine(object):
line = 'SomeProject' line = 'SomeProject'
filename = 'filename' filename = 'filename'
comes_from = '-r %s (line %s)' % (filename, 1) comes_from = '-r %s (line %s)' % (filename, 1)
req = InstallRequirement.from_line(line, comes_from=comes_from) req = install_req_from_line(line, comes_from=comes_from)
assert repr(list(process_line(line, filename, 1))[0]) == repr(req) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
def test_yield_line_constraint(self): def test_yield_line_constraint(self):
line = 'SomeProject' line = 'SomeProject'
filename = 'filename' filename = 'filename'
comes_from = '-c %s (line %s)' % (filename, 1) comes_from = '-c %s (line %s)' % (filename, 1)
req = InstallRequirement.from_line( req = install_req_from_line(
line, comes_from=comes_from, constraint=True) line, comes_from=comes_from, constraint=True)
found_req = list(process_line(line, filename, 1, constraint=True))[0] found_req = list(process_line(line, filename, 1, constraint=True))[0]
assert repr(found_req) == repr(req) assert repr(found_req) == repr(req)
@ -210,7 +211,7 @@ class TestProcessLine(object):
line = 'SomeProject >= 2' line = 'SomeProject >= 2'
filename = 'filename' filename = 'filename'
comes_from = '-r %s (line %s)' % (filename, 1) comes_from = '-r %s (line %s)' % (filename, 1)
req = InstallRequirement.from_line(line, comes_from=comes_from) req = install_req_from_line(line, comes_from=comes_from)
assert repr(list(process_line(line, filename, 1))[0]) == repr(req) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
assert str(req.req.specifier) == '>=2' assert str(req.req.specifier) == '>=2'
@ -235,7 +236,7 @@ class TestProcessLine(object):
def test_nested_requirements_file(self, monkeypatch): def test_nested_requirements_file(self, monkeypatch):
line = '-r another_file' line = '-r another_file'
req = InstallRequirement.from_line('SomeProject') req = install_req_from_line('SomeProject')
import pip._internal.req.req_file import pip._internal.req.req_file
def stub_parse_requirements(req_url, finder, comes_from, options, def stub_parse_requirements(req_url, finder, comes_from, options,
@ -248,7 +249,7 @@ class TestProcessLine(object):
def test_nested_constraints_file(self, monkeypatch): def test_nested_constraints_file(self, monkeypatch):
line = '-c another_file' line = '-c another_file'
req = InstallRequirement.from_line('SomeProject') req = install_req_from_line('SomeProject')
import pip._internal.req.req_file import pip._internal.req.req_file
def stub_parse_requirements(req_url, finder, comes_from, options, def stub_parse_requirements(req_url, finder, comes_from, options,

View File

@ -3,6 +3,7 @@ import tempfile
import pytest import pytest
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_install import InstallRequirement
@ -36,7 +37,7 @@ class TestInstallRequirementBuildDirectory(object):
with open(setup_py_path, 'w') as f: with open(setup_py_path, 'w') as f:
f.write('') f.write('')
requirement = InstallRequirement.from_line( requirement = install_req_from_line(
str(install_dir).replace(os.sep, os.altsep or os.sep) str(install_dir).replace(os.sep, os.altsep or os.sep)
) )