Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address #4759: change freeze to support editable Git repos with no remote #6095

Merged
merged 3 commits into from
Jan 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/4759.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Editable Git installs without a remote now freeze as editable.
11 changes: 10 additions & 1 deletion src/pip/_internal/operations/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def get_requirement_info(dist):

location = os.path.normcase(os.path.abspath(dist.location))

from pip._internal.vcs import vcs
from pip._internal.vcs import vcs, RemoteNotFoundError
vc_type = vcs.get_backend_type(location)

if not vc_type:
Expand All @@ -188,6 +188,15 @@ def get_requirement_info(dist):

try:
req = vc_type().get_src_requirement(location, dist.project_name)
except RemoteNotFoundError:
req = dist.as_requirement()
comments = [
'# Editable {} install with no remote ({})'.format(
vc_type.__name__, req,
)
]
return (location, True, comments)

except BadCommand:
logger.warning(
'cannot determine version of editable source in %s '
Expand Down
7 changes: 7 additions & 0 deletions src/pip/_internal/vcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
logger = logging.getLogger(__name__)


class RemoteNotFoundError(Exception):
pass


class RevOptions(object):

"""
Expand Down Expand Up @@ -452,6 +456,9 @@ def get_src_requirement(self, location, project_name):
def get_remote_url(self, location):
"""
Return the url used at location

Raises RemoteNotFoundError if the repository does not have a remote
url configured.
"""
raise NotImplementedError

Expand Down
23 changes: 17 additions & 6 deletions src/pip/_internal/vcs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
display_path, make_vcs_requirement_url, redact_password_from_url,
)
from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.vcs import VersionControl, vcs
from pip._internal.vcs import RemoteNotFoundError, VersionControl, vcs

urlsplit = urllib_parse.urlsplit
urlunsplit = urllib_parse.urlunsplit
Expand Down Expand Up @@ -250,13 +250,24 @@ def update(self, dest, url, rev_options):
self.update_submodules(dest)

def get_remote_url(self, location):
"""Return URL of the first remote encountered."""
remotes = self.run_command(
"""
Return URL of the first remote encountered.

Raises RemoteNotFoundError if the repository does not have a remote
url configured.
"""
# We need to pass 1 for extra_ok_returncodes since the command
# exits with return code 1 if there are no matching lines.
stdout = self.run_command(
['config', '--get-regexp', r'remote\..*\.url'],
show_stdout=False, cwd=location,
extra_ok_returncodes=(1, ), show_stdout=False, cwd=location,
)
remotes = remotes.splitlines()
found_remote = remotes[0]
remotes = stdout.splitlines()
try:
found_remote = remotes[0]
except IndexError:
raise RemoteNotFoundError

for remote in remotes:
if remote.startswith('remote.origin.url '):
found_remote = remote
Expand Down
20 changes: 20 additions & 0 deletions tests/functional/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ def test_freeze_editable_not_vcs(script, tmpdir):
_check_output(result.stdout, expected)


@pytest.mark.git
def test_freeze_editable_git_with_no_remote(script, tmpdir):
"""
Test an editable Git install with no remote url.
"""
pkg_path = _create_test_package(script)
script.pip('install', '-e', pkg_path)
result = script.pip('freeze')

assert result.stderr == ''

# We need to apply os.path.normcase() to the path since that is what
# the freeze code does.
expected = textwrap.dedent("""\
...# Editable Git install with no remote (version-pkg==0.1)
-e {}
...""".format(os.path.normcase(pkg_path)))
_check_output(result.stdout, expected)


@pytest.mark.svn
def test_freeze_svn(script, tmpdir):
"""Test freezing a svn checkout"""
Expand Down
14 changes: 0 additions & 14 deletions tests/functional/test_install_vcs_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,20 +367,6 @@ def test_git_with_ambiguous_revs(script):
result.assert_installed('version-pkg', with_files=['.git'])


def test_git_works_with_editable_non_origin_repo(script):
# set up, create a git repo and install it as editable from a local
# directory path
version_pkg_path = _create_test_package(script)
script.pip('install', '-e', version_pkg_path.abspath)

# 'freeze'ing this should not fall over, but should result in stderr output
# warning
result = script.pip('freeze', expect_stderr=True)
assert "Error when trying to get requirement" in result.stderr
assert "Could not determine repository location" in result.stdout
assert "version-pkg==0.1" in result.stdout


def test_editable__no_revision(script):
"""
Test a basic install in editable mode specifying no revision.
Expand Down
18 changes: 17 additions & 1 deletion tests/functional/test_vcs_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import os

from pip._internal.vcs.git import Git
import pytest

from pip._internal.vcs.git import Git, RemoteNotFoundError
from tests.lib import _create_test_package, _git_commit, _test_path_to_file_url


Expand Down Expand Up @@ -93,6 +95,20 @@ def test_get_remote_url(script, tmpdir):
assert remote_url == source_url


def test_get_remote_url__no_remote(script, tmpdir):
"""
Test a repo with no remote.
"""
repo_dir = tmpdir / 'temp-repo'
repo_dir.mkdir()
repo_dir = str(repo_dir)

script.run('git', 'init', cwd=repo_dir)

with pytest.raises(RemoteNotFoundError):
Git().get_remote_url(repo_dir)


def test_get_current_branch(script):
repo_dir = str(script.scratch_path)

Expand Down