From a6413f5d88f12466b3daa833668d0f59fc65ece4 Mon Sep 17 00:00:00 2001 From: Jason Chang Date: Wed, 26 Jul 2023 13:23:40 -0700 Subject: Update errors to extend BaseRepoError In order to better analyze and track repo errors, repo command failures need to be tied to specific errors in repo source code. Additionally a new GitCommandError was added to differentiate between general git related errors to failed git commands. Git commands that opt into verification will raise a GitCommandError if the command failed. The first step in this process is a general error refactoring Bug: b/293344017 Change-Id: I46944b1825ce892757c8dd3f7e2fab7e460760c0 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/380994 Commit-Queue: Jason Chang Reviewed-by: Aravind Vasudevan Tested-by: Jason Chang Reviewed-by: Joanna Wang --- error.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 27 deletions(-) (limited to 'error.py') diff --git a/error.py b/error.py index 3cf34d54..ed4a90b7 100644 --- a/error.py +++ b/error.py @@ -12,8 +12,51 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import List -class ManifestParseError(Exception): + +class BaseRepoError(Exception): + """All repo specific exceptions derive from BaseRepoError.""" + + +class RepoError(BaseRepoError): + """Exceptions thrown inside repo that can be handled.""" + + def __init__(self, *args, project: str = None) -> None: + super().__init__(*args) + self.project = project + + +class RepoExitError(BaseRepoError): + """Exception thrown that result in termination of repo program. + - Should only be handled in main.py + """ + + def __init__( + self, + *args, + exit_code: int = 1, + aggregate_errors: List[Exception] = None, + **kwargs, + ) -> None: + super().__init__(*args, **kwargs) + self.exit_code = exit_code + self.aggregate_errors = aggregate_errors + + +class RepoUnhandledExceptionError(RepoExitError): + """Exception that maintains error as reason for program exit.""" + + def __init__( + self, + error: BaseException, + **kwargs, + ) -> None: + super().__init__(error, **kwargs) + self.error = error + + +class ManifestParseError(RepoExitError): """Failed to parse the manifest file.""" @@ -25,11 +68,11 @@ class ManifestInvalidPathError(ManifestParseError): """A path used in or is incorrect.""" -class NoManifestException(Exception): +class NoManifestException(RepoExitError): """The required manifest does not exist.""" - def __init__(self, path, reason): - super().__init__(path, reason) + def __init__(self, path, reason, **kwargs): + super().__init__(path, reason, **kwargs) self.path = path self.reason = reason @@ -37,55 +80,64 @@ class NoManifestException(Exception): return self.reason -class EditorError(Exception): +class EditorError(RepoError): """Unspecified error from the user's text editor.""" - def __init__(self, reason): - super().__init__(reason) + def __init__(self, reason, **kwargs): + super().__init__(reason, **kwargs) self.reason = reason def __str__(self): return self.reason -class GitError(Exception): - """Unspecified internal error from git.""" +class GitError(RepoError): + """Unspecified git related error.""" - def __init__(self, command): - super().__init__(command) - self.command = command + def __init__(self, message, command_args=None, **kwargs): + super().__init__(message, **kwargs) + self.message = message + self.command_args = command_args def __str__(self): - return self.command + return self.message -class UploadError(Exception): +class UploadError(RepoError): """A bundle upload to Gerrit did not succeed.""" - def __init__(self, reason): - super().__init__(reason) + def __init__(self, reason, **kwargs): + super().__init__(reason, **kwargs) self.reason = reason def __str__(self): return self.reason -class DownloadError(Exception): +class DownloadError(RepoExitError): """Cannot download a repository.""" - def __init__(self, reason): - super().__init__(reason) + def __init__(self, reason, **kwargs): + super().__init__(reason, **kwargs) self.reason = reason def __str__(self): return self.reason -class NoSuchProjectError(Exception): +class SyncError(RepoExitError): + """Cannot sync repo.""" + + +class UpdateManifestError(RepoExitError): + """Cannot update manifest.""" + + +class NoSuchProjectError(RepoExitError): """A specified project does not exist in the work tree.""" - def __init__(self, name=None): - super().__init__(name) + def __init__(self, name=None, **kwargs): + super().__init__(**kwargs) self.name = name def __str__(self): @@ -94,11 +146,11 @@ class NoSuchProjectError(Exception): return self.name -class InvalidProjectGroupsError(Exception): +class InvalidProjectGroupsError(RepoExitError): """A specified project is not suitable for the specified groups""" - def __init__(self, name=None): - super().__init__(name) + def __init__(self, name=None, **kwargs): + super().__init__(**kwargs) self.name = name def __str__(self): @@ -107,7 +159,7 @@ class InvalidProjectGroupsError(Exception): return self.name -class RepoChangedException(Exception): +class RepoChangedException(BaseRepoError): """Thrown if 'repo sync' results in repo updating its internal repo or manifest repositories. In this special case we must use exec to re-execute repo with the new code and manifest. @@ -118,7 +170,7 @@ class RepoChangedException(Exception): self.extra_args = extra_args or [] -class HookError(Exception): +class HookError(RepoError): """Thrown if a 'repo-hook' could not be run. The common case is that the file wasn't present when we tried to run it. -- cgit v1.2.3-54-g00ecf