diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py index 6e1fd5b1a..549864936 100644 --- a/src/pip/_internal/cli/base_command.py +++ b/src/pip/_internal/cli/base_command.py @@ -22,9 +22,10 @@ from pip._internal.exceptions import ( ) from pip._internal.index import PackageFinder 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_install import InstallRequirement from pip._internal.utils.logging import setup_logging from pip._internal.utils.misc import get_prog, normalize_path from pip._internal.utils.outdated import pip_version_check @@ -209,7 +210,7 @@ class RequirementCommand(Command): requirement_set.add_requirement(req_to_add) for req in args: - req_to_add = InstallRequirement.from_line( + req_to_add = install_req_from_line( req, None, isolated=options.isolated_mode, wheel_cache=wheel_cache ) diff --git a/src/pip/_internal/commands/uninstall.py b/src/pip/_internal/commands/uninstall.py index a62bf3674..0cd6f54bd 100644 --- a/src/pip/_internal/commands/uninstall.py +++ b/src/pip/_internal/commands/uninstall.py @@ -4,7 +4,8 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command 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 @@ -47,7 +48,7 @@ class UninstallCommand(Command): with self._build_session(options) as session: reqs_to_uninstall = {} for name in args: - req = InstallRequirement.from_line( + req = install_req_from_line( name, isolated=options.isolated_mode, ) if req.name: diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py index 8ae8afe5d..1ceb7fedb 100644 --- a/src/pip/_internal/operations/freeze.py +++ b/src/pip/_internal/operations/freeze.py @@ -10,8 +10,9 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.pkg_resources import RequirementParseError from pip._internal.exceptions import InstallationError -from pip._internal.req import InstallRequirement -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 COMMENT_RE from pip._internal.utils.deprecation import deprecated from pip._internal.utils.misc import ( @@ -106,7 +107,7 @@ def freeze( wheel_cache=wheel_cache, ) else: - line_req = InstallRequirement.from_line( + line_req = install_req_from_line( COMMENT_RE.sub('', line).strip(), isolated=isolated, wheel_cache=wheel_cache, diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 770c66e37..b0e641bd7 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -8,18 +8,45 @@ These are meant to be used elsewhere within pip to create instances of InstallRequirement. """ +import logging 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.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements -# XXX: Temporarily importing _strip_extras -from pip._internal.download import path_to_url, url_to_path +from pip._internal.download import ( + is_archive_file, is_url, path_to_url, url_to_path, +) from pip._internal.exceptions import InstallationError 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.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): @@ -87,6 +114,36 @@ def parse_editable(editable_req): 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( editable_req, comes_from=None, isolated=False, options=None, wheel_cache=None, constraint=False @@ -114,3 +171,102 @@ def install_req_from_editable( wheel_cache=wheel_cache, 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, + ) diff --git a/src/pip/_internal/req/req_file.py b/src/pip/_internal/req/req_file.py index 17bd372de..e7acf7cb8 100644 --- a/src/pip/_internal/req/req_file.py +++ b/src/pip/_internal/req/req_file.py @@ -16,8 +16,9 @@ from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._internal.cli import cmdoptions from pip._internal.download import get_file_content from pip._internal.exceptions import RequirementsFileParseError -from pip._internal.req.constructors import install_req_from_editable -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) __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: if dest in opts.__dict__ and 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, isolated=isolated, options=req_options, wheel_cache=wheel_cache ) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index a2d8cc82e..c1989a50a 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -2,27 +2,21 @@ from __future__ import absolute_import import logging import os -import re import shutil import sys import sysconfig -import traceback import zipfile from distutils.util import change_root 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.utils import canonicalize_name from pip._vendor.packaging.version import Version from pip._vendor.packaging.version import parse as parse_version from pip._vendor.pep517.wrappers import Pep517HookCaller -from pip._vendor.pkg_resources import RequirementParseError, parse_requirements from pip._internal import wheel 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.locations import ( 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 ( _make_build_dir, ask_path_exists, backup_dir, call_subprocess, 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.setuptools_build import SETUPTOOLS_SHIM from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.ui import open_spinner 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__) -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): """ @@ -168,102 +148,6 @@ class InstallRequirement(object): 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): if self.req: s = str(self.req) @@ -998,30 +882,3 @@ class InstallRequirement(object): py_ver_str, self.name)] 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 diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index c9ce8b8d3..7fd47b17e 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -11,7 +11,7 @@ from tempfile import mkdtemp import pretend 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 tests.lib import assert_all_changes, create_test_package_with_setup 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) monkeypatch.setattr("pip._vendor.pkg_resources.get_distribution", get_dist) - req = InstallRequirement.from_line("thing") + req = install_req_from_line("thing") req.uninstall() assert os.path.exists(einfo) diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py index 51fccfae1..969f81e2d 100644 --- a/tests/unit/test_finder.py +++ b/tests/unit/test_finder.py @@ -14,13 +14,13 @@ from pip._internal.exceptions import ( from pip._internal.index import ( 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): """Finder skips zipfiles with "macosx10" in the name.""" finder = PackageFinder([data.find_links], [], session=PipSession()) - req = InstallRequirement.from_line("pkgwithmpkg") + req = install_req_from_line("pkgwithmpkg") found = finder.find_requirement(req, False) 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): """Finder requires the full project name to match, not just beginning.""" finder = PackageFinder([data.find_links], [], session=PipSession()) - req = InstallRequirement.from_line("gmpy") + req = install_req_from_line("gmpy") found = finder.find_requirement(req, False) assert found.url.endswith("gmpy-1.15.tar.gz"), found @@ -40,7 +40,7 @@ def test_tilde(): session = PipSession() with patch('pip._internal.index.os.path.exists', return_value=True): finder = PackageFinder(['~/python-pkgs'], [], session=session) - req = InstallRequirement.from_line("gmpy") + req = install_req_from_line("gmpy") with pytest.raises(DistributionNotFound): finder.find_requirement(req, False) @@ -53,7 +53,7 @@ def test_duplicates_sort_ok(data): [], session=PipSession(), ) - req = InstallRequirement.from_line("duplicate") + req = install_req_from_line("duplicate") found = finder.find_requirement(req, False) 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): """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()) link = finder.find_requirement(req, False) 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): """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()) link = finder.find_requirement(req, False) assert link.url.endswith("Dinner-2.0.tar.gz") @@ -78,7 +78,7 @@ def test_incorrect_case_file_index(data): @pytest.mark.network def test_finder_detects_latest_already_satisfied_find_links(data): """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 latest_version = "3.0" satisfied_by = Mock( @@ -96,7 +96,7 @@ def test_finder_detects_latest_already_satisfied_find_links(data): @pytest.mark.network def test_finder_detects_latest_already_satisfied_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 latest_version = "0.3.1" satisfied_by = Mock( @@ -123,7 +123,7 @@ class TestWheel: """ 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 finder = PackageFinder( [data.find_links], @@ -148,7 +148,7 @@ class TestWheel: lambda **kw: [("py1", "none", "any")], ) - req = InstallRequirement.from_line("simple.dist") + req = install_req_from_line("simple.dist") finder = PackageFinder( [data.find_links], [], @@ -169,7 +169,7 @@ class TestWheel: lambda **kw: [('py2', 'none', 'any')], ) - req = InstallRequirement.from_line("simple.dist") + req = install_req_from_line("simple.dist") finder = PackageFinder( [data.find_links], [], @@ -185,7 +185,7 @@ class TestWheel: Test wheels have priority over sdists. `test_link_sorting` also covers this at lower level """ - req = InstallRequirement.from_line("priority") + req = install_req_from_line("priority") finder = PackageFinder( [data.find_links], [], @@ -199,7 +199,7 @@ class TestWheel: Test existing install has priority over wheels. `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" satisfied_by = Mock( location="/path", @@ -284,7 +284,7 @@ class TestWheel: def test_finder_priority_file_over_page(data): """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( [data.find_links], ["http://pypi.org/simple/"], @@ -304,7 +304,7 @@ def test_finder_deplink(): """ 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( [], [], @@ -323,7 +323,7 @@ def test_finder_priority_page_over_deplink(): """ 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( [], ["https://pypi.org/simple/"], @@ -346,7 +346,7 @@ def test_finder_priority_page_over_deplink(): def test_finder_priority_nonegg_over_eggfragments(): """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'] 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. """ - req = InstallRequirement.from_line("bar", None) + req = install_req_from_line("bar", None) # using a local index (that has pre & dev releases) 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. """ - req = InstallRequirement.from_line("bar", None) + req = install_req_from_line("bar", None) # using a local index (that has pre & dev releases) finder = PackageFinder( @@ -471,7 +471,7 @@ def test_finder_installs_dev_releases(data): 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) finder = PackageFinder( @@ -487,7 +487,7 @@ def test_finder_installs_pre_releases_with_version_spec(): """ 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"] finder = PackageFinder(links, [], session=PipSession()) @@ -555,7 +555,7 @@ def test_get_index_urls_locations(): finder = PackageFinder( [], ['file://index1/', 'file://index2'], session=PipSession()) 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/', 'file://index2/complex-name/'] diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index 30110f149..09941d0f9 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -18,7 +18,7 @@ from pip._internal.index import PackageFinder from pip._internal.operations.prepare import RequirementPreparer from pip._internal.req import InstallRequirement, RequirementSet 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_tracker import RequirementTracker @@ -68,7 +68,7 @@ class TestRequirementSet(object): os.makedirs(build_dir) open(os.path.join(build_dir, "setup.py"), 'w') reqset = RequirementSet() - req = InstallRequirement.from_line('simple') + req = install_req_from_line('simple') req.is_direct = True reqset.add_requirement(req) finder = PackageFinder([data.find_links], [], session=PipSession()) @@ -341,12 +341,12 @@ class TestInstallRequirement(object): """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) + req = install_req_from_line(url + fragment) assert req.link.url == url + fragment, req.link def test_unsupported_wheel_link_requirement_raises(self): reqset = RequirementSet() - req = InstallRequirement.from_line( + req = install_req_from_line( 'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl', ) assert req.link is not None @@ -358,7 +358,7 @@ class TestInstallRequirement(object): def test_unsupported_wheel_local_file_requirement_raises(self, data): reqset = RequirementSet() - req = InstallRequirement.from_line( + req = install_req_from_line( data.packages.join('simple.dist-0.1-py1-none-invalid.whl'), ) assert req.link is not None @@ -369,32 +369,32 @@ class TestInstallRequirement(object): reqset.add_requirement(req) 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 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' def test_repr(self): - req = InstallRequirement.from_line('simple==0.1') + req = install_req_from_line('simple==0.1') assert repr(req) == ( '' ) def test_invalid_wheel_requirement_raises(self): with pytest.raises(InvalidWheelFilename): - InstallRequirement.from_line('invalid.whl') + install_req_from_line('invalid.whl') 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 str(req.req) == 'simple==0.1' def test_url_preserved_line_req(self): """Confirm the url is preserved in a non-editable requirement""" url = 'git+http://foo.com@ref#egg=foo' - req = InstallRequirement.from_line(url) + req = install_req_from_line(url) assert req.link.url == url def test_url_preserved_editable_req(self): @@ -409,7 +409,7 @@ class TestInstallRequirement(object): '/path/to/foo.egg-info/'.replace('/', os.path.sep), )) def test_get_dist(self, path): - req = InstallRequirement.from_line('foo') + req = install_req_from_line('foo') req._egg_info_path = path dist = req.get_dist() assert isinstance(dist, pkg_resources.Distribution) @@ -425,14 +425,14 @@ class TestInstallRequirement(object): # without spaces 'mock3;python_version >= "3"', ): - req = InstallRequirement.from_line(line) + req = install_req_from_line(line) assert req.req.name == 'mock3' assert str(req.req.specifier) == '' assert str(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"') + req = install_req_from_line('semicolon; os_name == "a; b"') assert req.req.name == 'semicolon' assert str(req.req.specifier) == '' assert str(req.markers) == 'os_name == "a; b"' @@ -441,14 +441,14 @@ class TestInstallRequirement(object): # 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) + req = install_req_from_line(line) assert req.link.url == url, req.url assert str(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) + req = install_req_from_line(line) assert req.link.url == line, req.url assert req.markers is None @@ -459,7 +459,7 @@ class TestInstallRequirement(object): 'sys_platform == %r' % sys.platform, ): line = 'name; ' + markers - req = InstallRequirement.from_line(line) + req = install_req_from_line(line) assert str(req.markers) == str(Marker(markers)) assert req.match_markers() @@ -469,7 +469,7 @@ class TestInstallRequirement(object): 'sys_platform != %r' % sys.platform, ): line = 'name; ' + markers - req = InstallRequirement.from_line(line) + req = install_req_from_line(line) assert str(req.markers) == str(Marker(markers)) assert not req.match_markers() @@ -480,7 +480,7 @@ class TestInstallRequirement(object): 'sys_platform == %r' % sys.platform, ): 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 req.match_markers() @@ -490,7 +490,7 @@ class TestInstallRequirement(object): 'sys_platform != %r' % sys.platform, ): 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 not req.match_markers() @@ -498,7 +498,7 @@ class TestInstallRequirement(object): line = 'SomeProject[ex1,ex2]' filename = 'filename' 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 req.extras == {'ex1', 'ex2'} @@ -506,7 +506,7 @@ class TestInstallRequirement(object): 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) + req = install_req_from_line(line, comes_from=comes_from) assert len(req.extras) == 2 assert req.extras == {'ex1', 'ex2'} @@ -528,7 +528,7 @@ class TestInstallRequirement(object): def test_unexisting_path(self): with pytest.raises(InstallationError) as e: - InstallRequirement.from_line( + install_req_from_line( os.path.join('this', 'path', 'does', 'not', 'exist')) err_msg = e.value.args[0] assert "Invalid requirement" in err_msg @@ -536,14 +536,14 @@ class TestInstallRequirement(object): def test_single_equal_sign(self): with pytest.raises(InstallationError) as e: - InstallRequirement.from_line('toto=42') + install_req_from_line('toto=42') err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "= is not a valid operator. Did you mean == ?" in err_msg def test_traceback(self): with pytest.raises(InstallationError) as e: - InstallRequirement.from_line('toto 42') + install_req_from_line('toto 42') err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "\nTraceback " in err_msg @@ -553,7 +553,7 @@ class TestInstallRequirement(object): with open(req_file_path, 'w') as req_file: req_file.write('pip\nsetuptools') 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] assert "Invalid requirement" 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(): """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'") eq26.is_direct = True - ne26 = InstallRequirement.from_line( + ne26 = install_req_from_line( "Django>=1.6.10,<1.8 ; python_version != '2.6'") ne26.is_direct = True diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py index 664196b03..1d7c48b7f 100644 --- a/tests/unit/test_req_file.py +++ b/tests/unit/test_req_file.py @@ -12,12 +12,13 @@ from pip._internal.exceptions import ( InstallationError, RequirementsFileParseError, ) 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 ( break_args_options, ignore_comments, join_lines, parse_requirements, preprocess, process_line, skip_regex, ) -from pip._internal.req.req_install import InstallRequirement from tests.lib import requirements_file @@ -193,14 +194,14 @@ class TestProcessLine(object): line = 'SomeProject' filename = 'filename' 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) def test_yield_line_constraint(self): line = 'SomeProject' filename = 'filename' comes_from = '-c %s (line %s)' % (filename, 1) - req = InstallRequirement.from_line( + req = install_req_from_line( line, comes_from=comes_from, constraint=True) found_req = list(process_line(line, filename, 1, constraint=True))[0] assert repr(found_req) == repr(req) @@ -210,7 +211,7 @@ class TestProcessLine(object): line = 'SomeProject >= 2' filename = 'filename' 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 str(req.req.specifier) == '>=2' @@ -235,7 +236,7 @@ class TestProcessLine(object): def test_nested_requirements_file(self, monkeypatch): line = '-r another_file' - req = InstallRequirement.from_line('SomeProject') + req = install_req_from_line('SomeProject') import pip._internal.req.req_file def stub_parse_requirements(req_url, finder, comes_from, options, @@ -248,7 +249,7 @@ class TestProcessLine(object): def test_nested_constraints_file(self, monkeypatch): line = '-c another_file' - req = InstallRequirement.from_line('SomeProject') + req = install_req_from_line('SomeProject') import pip._internal.req.req_file def stub_parse_requirements(req_url, finder, comes_from, options, diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py index 17af7d5f5..f4a6e94a1 100644 --- a/tests/unit/test_req_install.py +++ b/tests/unit/test_req_install.py @@ -3,6 +3,7 @@ import tempfile import pytest +from pip._internal.req.constructors import install_req_from_line from pip._internal.req.req_install import InstallRequirement @@ -36,7 +37,7 @@ class TestInstallRequirementBuildDirectory(object): with open(setup_py_path, 'w') as f: f.write('') - requirement = InstallRequirement.from_line( + requirement = install_req_from_line( str(install_dir).replace(os.sep, os.altsep or os.sep) )