From 520df5356ffd3c9a9b0da0f624a070699b1c7839 Mon Sep 17 00:00:00 2001 From: Chris Jerdonek Date: Mon, 24 Sep 2018 14:53:39 -0700 Subject: [PATCH 1/2] Add misc.make_vcs_requirement_url(). --- ...6F9BDF-53AF-4885-A966-6474A27A6D46.trivial | 0 src/pip/_internal/operations/freeze.py | 9 +++---- src/pip/_internal/utils/misc.py | 14 ++++++++++ src/pip/_internal/vcs/bazaar.py | 8 +++--- src/pip/_internal/vcs/git.py | 12 ++++----- src/pip/_internal/vcs/mercurial.py | 7 ++--- src/pip/_internal/vcs/subversion.py | 7 ++--- tests/unit/test_utils.py | 26 +++++++++++++++++-- 8 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 news/516F9BDF-53AF-4885-A966-6474A27A6D46.trivial diff --git a/news/516F9BDF-53AF-4885-A966-6474A27A6D46.trivial b/news/516F9BDF-53AF-4885-A966-6474A27A6D46.trivial new file mode 100644 index 000000000..e69de29bb diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py index 1ceb7fedb..aeeb86910 100644 --- a/src/pip/_internal/operations/freeze.py +++ b/src/pip/_internal/operations/freeze.py @@ -16,7 +16,7 @@ from pip._internal.req.constructors import ( from pip._internal.req.req_file import COMMENT_RE from pip._internal.utils.deprecation import deprecated from pip._internal.utils.misc import ( - dist_is_editable, get_installed_distributions, + dist_is_editable, get_installed_distributions, make_vcs_requirement_url, ) logger = logging.getLogger(__name__) @@ -233,11 +233,8 @@ class FrozenRequirement(object): else: rev = '{%s}' % date_match.group(1) editable = True - req = '%s@%s#egg=%s' % ( - svn_location, - rev, - cls.egg_name(dist) - ) + egg_name = cls.egg_name(dist) + req = make_vcs_requirement_url(svn_location, rev, egg_name) return cls(dist.project_name, req, editable, comments) @staticmethod diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index f7afd1df5..84a421fe4 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -856,6 +856,20 @@ def enum(*sequential, **named): return type('Enum', (), enums) +def make_vcs_requirement_url(repo_url, rev, egg_project_name, subdir=None): + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + """ + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + def split_auth_from_netloc(netloc): """ Parse out and remove the auth information from a netloc. diff --git a/src/pip/_internal/vcs/bazaar.py b/src/pip/_internal/vcs/bazaar.py index d5c6efaf5..3cc66c9dc 100644 --- a/src/pip/_internal/vcs/bazaar.py +++ b/src/pip/_internal/vcs/bazaar.py @@ -6,7 +6,9 @@ import os from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._internal.download import path_to_url -from pip._internal.utils.misc import display_path, rmtree +from pip._internal.utils.misc import ( + display_path, make_vcs_requirement_url, rmtree, +) from pip._internal.utils.temp_dir import TempDirectory from pip._internal.vcs import VersionControl, vcs @@ -98,9 +100,9 @@ class Bazaar(VersionControl): return None if not repo.lower().startswith('bzr:'): repo = 'bzr+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] current_rev = self.get_revision(location) - return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev, egg_project_name) def is_commit_id_equal(self, dest, name): """Always assume the versions don't match""" diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py index d8fd2df19..977853946 100644 --- a/src/pip/_internal/vcs/git.py +++ b/src/pip/_internal/vcs/git.py @@ -10,7 +10,7 @@ from pip._vendor.six.moves.urllib import request as urllib_request from pip._internal.exceptions import BadCommand from pip._internal.utils.compat import samefile -from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import display_path, make_vcs_requirement_url from pip._internal.utils.temp_dir import TempDirectory from pip._internal.vcs import VersionControl, vcs @@ -294,12 +294,12 @@ class Git(VersionControl): repo = self.get_url(location) if not repo.lower().startswith('git:'): repo = 'git+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] current_rev = self.get_revision(location) - req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) - subdirectory = self._get_subdirectory(location) - if subdirectory: - req += '&subdirectory=' + subdirectory + egg_project_name = dist.egg_name().split('-', 1)[0] + subdir = self._get_subdirectory(location) + req = make_vcs_requirement_url(repo, current_rev, egg_project_name, + subdir=subdir) + return req def get_url_rev_and_auth(self, url): diff --git a/src/pip/_internal/vcs/mercurial.py b/src/pip/_internal/vcs/mercurial.py index 49039a912..17cfb67d1 100644 --- a/src/pip/_internal/vcs/mercurial.py +++ b/src/pip/_internal/vcs/mercurial.py @@ -6,7 +6,7 @@ import os from pip._vendor.six.moves import configparser from pip._internal.download import path_to_url -from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import display_path, make_vcs_requirement_url from pip._internal.utils.temp_dir import TempDirectory from pip._internal.vcs import VersionControl, vcs @@ -88,9 +88,10 @@ class Mercurial(VersionControl): repo = self.get_url(location) if not repo.lower().startswith('hg:'): repo = 'hg+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] current_rev_hash = self.get_revision_hash(location) - return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name) + egg_project_name = dist.egg_name().split('-', 1)[0] + return make_vcs_requirement_url(repo, current_rev_hash, + egg_project_name) def is_commit_id_equal(self, dest, name): """Always assume the versions don't match""" diff --git a/src/pip/_internal/vcs/subversion.py b/src/pip/_internal/vcs/subversion.py index 19e2e70dc..6f7cb5d94 100644 --- a/src/pip/_internal/vcs/subversion.py +++ b/src/pip/_internal/vcs/subversion.py @@ -7,7 +7,7 @@ import re from pip._internal.models.link import Link from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ( - display_path, rmtree, split_auth_from_netloc, + display_path, make_vcs_requirement_url, rmtree, split_auth_from_netloc, ) from pip._internal.vcs import VersionControl, vcs @@ -199,10 +199,11 @@ class Subversion(VersionControl): repo = self.get_url(location) if repo is None: return None + repo = 'svn+' + repo + rev = self.get_revision(location) # FIXME: why not project name? egg_project_name = dist.egg_name().split('-', 1)[0] - rev = self.get_revision(location) - return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name) + return make_vcs_requirement_url(repo, rev, egg_project_name) def is_commit_id_equal(self, dest, name): """Always assume the versions don't match""" diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 947d25892..972de2e0e 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -24,8 +24,8 @@ from pip._internal.utils.glibc import check_glibc_version from pip._internal.utils.hashes import Hashes, MissingHashes from pip._internal.utils.misc import ( call_subprocess, egg_link_path, ensure_dir, get_installed_distributions, - get_prog, normalize_path, remove_auth_from_url, rmtree, - split_auth_from_netloc, untar_file, unzip_file, + get_prog, make_vcs_requirement_url, normalize_path, remove_auth_from_url, + rmtree, split_auth_from_netloc, untar_file, unzip_file, ) from pip._internal.utils.packaging import check_dist_requires_python from pip._internal.utils.temp_dir import TempDirectory @@ -627,6 +627,28 @@ def test_call_subprocess_closes_stdin(): call_subprocess([sys.executable, '-c', 'input()']) +@pytest.mark.parametrize('args, expected', [ + # Test without subdir. + (('git+https://example.com/pkg', 'dev', 'myproj'), + 'git+https://example.com/pkg@dev#egg=myproj'), + # Test with subdir. + (('git+https://example.com/pkg', 'dev', 'myproj', 'sub/dir'), + 'git+https://example.com/pkg@dev#egg=myproj&subdirectory=sub/dir'), + # Test with None subdir. + (('git+https://example.com/pkg', 'dev', 'myproj', None), + 'git+https://example.com/pkg@dev#egg=myproj'), +]) +def test_make_vcs_requirement_url(args, expected): + if len(args) == 3: + url, rev, egg_name = args + actual = make_vcs_requirement_url(url, rev, egg_name) + else: + url, rev, egg_name, subdir = args + actual = make_vcs_requirement_url(url, rev, egg_name, subdir=subdir) + + assert actual == expected + + @pytest.mark.parametrize('netloc, expected', [ # Test a basic case. ('example.com', ('example.com', (None, None))), From 4afc1e3f69c81a149353e422bd39ae1a08641136 Mon Sep 17 00:00:00 2001 From: Chris Jerdonek Date: Tue, 25 Sep 2018 02:23:32 -0700 Subject: [PATCH 2/2] Use *args in the test. This incorporates a review suggestion of @pradyunsg. --- tests/unit/test_utils.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 972de2e0e..591c8ab54 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -639,13 +639,7 @@ def test_call_subprocess_closes_stdin(): 'git+https://example.com/pkg@dev#egg=myproj'), ]) def test_make_vcs_requirement_url(args, expected): - if len(args) == 3: - url, rev, egg_name = args - actual = make_vcs_requirement_url(url, rev, egg_name) - else: - url, rev, egg_name, subdir = args - actual = make_vcs_requirement_url(url, rev, egg_name, subdir=subdir) - + actual = make_vcs_requirement_url(*args) assert actual == expected