diff --git a/docs/reference/pip_install.rst b/docs/reference/pip_install.rst index 66f7492529a..c323035b408 100644 --- a/docs/reference/pip_install.rst +++ b/docs/reference/pip_install.rst @@ -426,6 +426,12 @@ Subversion pip supports the URL schemes ``svn``, ``svn+svn``, ``svn+http``, ``svn+https``, ``svn+ssh``. +Here are some of the supported forms:: + + [-e] svn+https://svn.example.com/MyProject#egg=MyProject + [-e] svn+ssh://svn.example.com/MyProject#egg=MyProject + [-e] svn+ssh://user@svn.example.com/MyProject#egg=MyProject + You can also give specific revisions to an SVN URL, like so:: [-e] svn+svn://svn.example.com/svn/MyProject#egg=MyProject diff --git a/news/5375.feature b/news/5375.feature new file mode 100644 index 00000000000..5e6460d3dea --- /dev/null +++ b/news/5375.feature @@ -0,0 +1 @@ +Support passing ``svn+ssh`` URLs with a username to ``pip install -e``. diff --git a/src/pip/_internal/vcs/__init__.py b/src/pip/_internal/vcs/__init__.py index 7d953c77e8f..cc25d37dea8 100644 --- a/src/pip/_internal/vcs/__init__.py +++ b/src/pip/_internal/vcs/__init__.py @@ -207,11 +207,15 @@ def export(self, location): """ raise NotImplementedError - def get_netloc_and_auth(self, netloc): + def get_netloc_and_auth(self, netloc, scheme): """ Parse the repository URL's netloc, and return the new netloc to use along with auth information. + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + This is mainly for the Subversion class to override, so that auth information can be provided via the --username and --password options instead of through the URL. For other subclasses like Git without @@ -237,7 +241,7 @@ def get_url_rev_and_auth(self, url): ) # Remove the vcs prefix. scheme = scheme.split('+', 1)[1] - netloc, user_pass = self.get_netloc_and_auth(netloc) + netloc, user_pass = self.get_netloc_and_auth(netloc, scheme) rev = None if '@' in path: path, rev = path.rsplit('@', 1) diff --git a/src/pip/_internal/vcs/subversion.py b/src/pip/_internal/vcs/subversion.py index 9f27dd5dd2b..bce95623551 100644 --- a/src/pip/_internal/vcs/subversion.py +++ b/src/pip/_internal/vcs/subversion.py @@ -102,11 +102,17 @@ def get_revision(self, location): revision = max(revision, localrev) return revision - def get_netloc_and_auth(self, netloc): + def get_netloc_and_auth(self, netloc, scheme): """ This override allows the auth information to be passed to svn via the --username and --password options instead of via the URL. """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, self).get_netloc_and_auth( + netloc, scheme) + return split_auth_from_netloc(netloc) def get_url_rev_and_auth(self, url): diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index b46f11cd97a..8c978e54abd 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -124,33 +124,39 @@ def test_git_is_commit_id_equal(git, rev_name, result): # The non-SVN backends all use the same get_netloc_and_auth(), so only test # Git as a representative. -@pytest.mark.parametrize('netloc, expected', [ +@pytest.mark.parametrize('args, expected', [ # Test a basic case. - ('example.com', ('example.com', (None, None))), + (('example.com', 'https'), ('example.com', (None, None))), # Test with username and password. - ('user:pass@example.com', ('user:pass@example.com', (None, None))), + (('user:pass@example.com', 'https'), + ('user:pass@example.com', (None, None))), ]) -def test_git__get_netloc_and_auth(netloc, expected): +def test_git__get_netloc_and_auth(args, expected): """ Test VersionControl.get_netloc_and_auth(). """ - actual = Git().get_netloc_and_auth(netloc) + netloc, scheme = args + actual = Git().get_netloc_and_auth(netloc, scheme) assert actual == expected -@pytest.mark.parametrize('netloc, expected', [ - # Test a basic case. - ('example.com', ('example.com', (None, None))), - # Test with username and no password. - ('user@example.com', ('example.com', ('user', None))), - # Test with username and password. - ('user:pass@example.com', ('example.com', ('user', 'pass'))), +@pytest.mark.parametrize('args, expected', [ + # Test https. + (('example.com', 'https'), ('example.com', (None, None))), + # Test https with username and no password. + (('user@example.com', 'https'), ('example.com', ('user', None))), + # Test https with username and password. + (('user:pass@example.com', 'https'), ('example.com', ('user', 'pass'))), + # Test ssh with username and password. + (('user:pass@example.com', 'ssh'), + ('user:pass@example.com', (None, None))), ]) -def test_subversion__get_netloc_and_auth(netloc, expected): +def test_subversion__get_netloc_and_auth(args, expected): """ Test Subversion.get_netloc_and_auth(). """ - actual = Subversion().get_netloc_and_auth(netloc) + netloc, scheme = args + actual = Subversion().get_netloc_and_auth(netloc, scheme) assert actual == expected @@ -247,6 +253,28 @@ def test_bazaar__get_url_rev_and_auth(): ) +@pytest.mark.parametrize('url, expected', [ + # Test an https URL. + ('svn+https://svn.example.com/MyProject#egg=MyProject', + ('https://svn.example.com/MyProject', None, (None, None))), + # Test an https URL with a username and password. + ('svn+https://user:pass@svn.example.com/MyProject#egg=MyProject', + ('https://svn.example.com/MyProject', None, ('user', 'pass'))), + # Test an ssh URL. + ('svn+ssh://svn.example.com/MyProject#egg=MyProject', + ('svn+ssh://svn.example.com/MyProject', None, (None, None))), + # Test an ssh URL with a username. + ('svn+ssh://user@svn.example.com/MyProject#egg=MyProject', + ('svn+ssh://user@svn.example.com/MyProject', None, (None, None))), +]) +def test_subversion__get_url_rev_and_auth(url, expected): + """ + Test Subversion.get_url_rev_and_auth(). + """ + actual = Subversion().get_url_rev_and_auth(url) + assert actual == expected + + # The non-SVN backends all use the same make_rev_args(), so only test # Git as a representative. @pytest.mark.parametrize('username, password, expected', [