Skip to content

Let remote.push return a PushInfoList #1360

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

Merged
merged 8 commits into from
Nov 13, 2021
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
23 changes: 20 additions & 3 deletions git/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,22 @@ def iter_items(cls, repo: 'Repo', *args: Any, **kwargs: Any
raise NotImplementedError


class PushInfoList(IterableList[PushInfo]):
def __new__(cls) -> 'PushInfoList':
return cast(PushInfoList, IterableList.__new__(cls, 'push_infos'))

def __init__(self) -> None:
super().__init__('push_infos')
self.error: Optional[Exception] = None

def raise_if_error(self) -> None:
"""
Raise an exception if any ref failed to push.
"""
if self.error:
raise self.error


class FetchInfo(IterableObj, object):

"""
Expand Down Expand Up @@ -774,15 +790,15 @@ def _get_fetch_info_from_stderr(self, proc: 'Git.AutoInterrupt',

def _get_push_info(self, proc: 'Git.AutoInterrupt',
progress: Union[Callable[..., Any], RemoteProgress, None],
kill_after_timeout: Union[None, float] = None) -> IterableList[PushInfo]:
kill_after_timeout: Union[None, float] = None) -> PushInfoList:
progress = to_progress_instance(progress)

# read progress information from stderr
# we hope stdout can hold all the data, it should ...
# read the lines manually as it will use carriage returns between the messages
# to override the previous one. This is why we read the bytes manually
progress_handler = progress.new_message_handler()
output: IterableList[PushInfo] = IterableList('push_infos')
output: PushInfoList = PushInfoList()

def stdout_handler(line: str) -> None:
try:
Expand All @@ -796,13 +812,14 @@ def stdout_handler(line: str) -> None:
stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or ''
try:
proc.wait(stderr=stderr_text)
except Exception:
except Exception as e:
# This is different than fetch (which fails if there is any std_err
# even if there is an output)
if not output:
raise
elif stderr_text:
log.warning("Error lines received while fetching: %s", stderr_text)
output.error = e

return output

Expand Down
3 changes: 2 additions & 1 deletion test/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ def test_references_and_objects(self, rw_dir):
origin.rename('new_origin')
# push and pull behaves similarly to `git push|pull`
origin.pull()
origin.push()
origin.push() # attempt push, ignore errors
origin.push().raise_if_error() # push and raise error if it fails
# assert not empty_repo.delete_remote(origin).exists() # create and delete remotes
# ![25-test_references_and_objects]

Expand Down
11 changes: 10 additions & 1 deletion test/test_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
fixture,
GIT_DAEMON_PORT
)
from git.util import rmtree, HIDE_WINDOWS_FREEZE_ERRORS
from git.util import rmtree, HIDE_WINDOWS_FREEZE_ERRORS, IterableList
import os.path as osp


Expand Down Expand Up @@ -128,6 +128,9 @@ def _do_test_fetch_result(self, results, remote):
# END for each info

def _do_test_push_result(self, results, remote):
self.assertIsInstance(results, list)
self.assertIsInstance(results, IterableList)

self.assertGreater(len(results), 0)
self.assertIsInstance(results[0], PushInfo)
for info in results:
Expand All @@ -151,6 +154,12 @@ def _do_test_push_result(self, results, remote):
# END error checking
# END for each info

if any([info.flags & info.ERROR for info in results]):
self.assertRaises(GitCommandError, results.raise_if_error)
else:
# No errors, so this should do nothing
results.raise_if_error()

def _do_test_fetch_info(self, repo):
self.assertRaises(ValueError, FetchInfo._from_line, repo, "nonsense", '')
self.assertRaises(
Expand Down