diff options
| author | Jason Chang <jasonnc@google.com> | 2023-07-26 13:23:40 -0700 |
|---|---|---|
| committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-07-31 21:31:36 +0000 |
| commit | a6413f5d88f12466b3daa833668d0f59fc65ece4 (patch) | |
| tree | 25410555a8941c500fbd55a974476ace04198dca /error.py | |
| parent | 8c35d948cfa76ec685ad36fb1cb3a0fcc749f428 (diff) | |
| download | git-repo-a6413f5d88f12466b3daa833668d0f59fc65ece4.tar.gz | |
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 <jasonnc@google.com>
Reviewed-by: Aravind Vasudevan <aravindvasudev@google.com>
Tested-by: Jason Chang <jasonnc@google.com>
Reviewed-by: Joanna Wang <jojwang@google.com>
Diffstat (limited to 'error.py')
| -rw-r--r-- | error.py | 106 |
1 files changed, 79 insertions, 27 deletions
| @@ -12,8 +12,51 @@ | |||
| 12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. | 13 | # limitations under the License. |
| 14 | 14 | ||
| 15 | from typing import List | ||
| 15 | 16 | ||
| 16 | class ManifestParseError(Exception): | 17 | |
| 18 | class BaseRepoError(Exception): | ||
| 19 | """All repo specific exceptions derive from BaseRepoError.""" | ||
| 20 | |||
| 21 | |||
| 22 | class RepoError(BaseRepoError): | ||
| 23 | """Exceptions thrown inside repo that can be handled.""" | ||
| 24 | |||
| 25 | def __init__(self, *args, project: str = None) -> None: | ||
| 26 | super().__init__(*args) | ||
| 27 | self.project = project | ||
| 28 | |||
| 29 | |||
| 30 | class RepoExitError(BaseRepoError): | ||
| 31 | """Exception thrown that result in termination of repo program. | ||
| 32 | - Should only be handled in main.py | ||
| 33 | """ | ||
| 34 | |||
| 35 | def __init__( | ||
| 36 | self, | ||
| 37 | *args, | ||
| 38 | exit_code: int = 1, | ||
| 39 | aggregate_errors: List[Exception] = None, | ||
| 40 | **kwargs, | ||
| 41 | ) -> None: | ||
| 42 | super().__init__(*args, **kwargs) | ||
| 43 | self.exit_code = exit_code | ||
| 44 | self.aggregate_errors = aggregate_errors | ||
| 45 | |||
| 46 | |||
| 47 | class RepoUnhandledExceptionError(RepoExitError): | ||
| 48 | """Exception that maintains error as reason for program exit.""" | ||
| 49 | |||
| 50 | def __init__( | ||
| 51 | self, | ||
| 52 | error: BaseException, | ||
| 53 | **kwargs, | ||
| 54 | ) -> None: | ||
| 55 | super().__init__(error, **kwargs) | ||
| 56 | self.error = error | ||
| 57 | |||
| 58 | |||
| 59 | class ManifestParseError(RepoExitError): | ||
| 17 | """Failed to parse the manifest file.""" | 60 | """Failed to parse the manifest file.""" |
| 18 | 61 | ||
| 19 | 62 | ||
| @@ -25,11 +68,11 @@ class ManifestInvalidPathError(ManifestParseError): | |||
| 25 | """A path used in <copyfile> or <linkfile> is incorrect.""" | 68 | """A path used in <copyfile> or <linkfile> is incorrect.""" |
| 26 | 69 | ||
| 27 | 70 | ||
| 28 | class NoManifestException(Exception): | 71 | class NoManifestException(RepoExitError): |
| 29 | """The required manifest does not exist.""" | 72 | """The required manifest does not exist.""" |
| 30 | 73 | ||
| 31 | def __init__(self, path, reason): | 74 | def __init__(self, path, reason, **kwargs): |
| 32 | super().__init__(path, reason) | 75 | super().__init__(path, reason, **kwargs) |
| 33 | self.path = path | 76 | self.path = path |
| 34 | self.reason = reason | 77 | self.reason = reason |
| 35 | 78 | ||
| @@ -37,55 +80,64 @@ class NoManifestException(Exception): | |||
| 37 | return self.reason | 80 | return self.reason |
| 38 | 81 | ||
| 39 | 82 | ||
| 40 | class EditorError(Exception): | 83 | class EditorError(RepoError): |
| 41 | """Unspecified error from the user's text editor.""" | 84 | """Unspecified error from the user's text editor.""" |
| 42 | 85 | ||
| 43 | def __init__(self, reason): | 86 | def __init__(self, reason, **kwargs): |
| 44 | super().__init__(reason) | 87 | super().__init__(reason, **kwargs) |
| 45 | self.reason = reason | 88 | self.reason = reason |
| 46 | 89 | ||
| 47 | def __str__(self): | 90 | def __str__(self): |
| 48 | return self.reason | 91 | return self.reason |
| 49 | 92 | ||
| 50 | 93 | ||
| 51 | class GitError(Exception): | 94 | class GitError(RepoError): |
| 52 | """Unspecified internal error from git.""" | 95 | """Unspecified git related error.""" |
| 53 | 96 | ||
| 54 | def __init__(self, command): | 97 | def __init__(self, message, command_args=None, **kwargs): |
| 55 | super().__init__(command) | 98 | super().__init__(message, **kwargs) |
| 56 | self.command = command | 99 | self.message = message |
| 100 | self.command_args = command_args | ||
| 57 | 101 | ||
| 58 | def __str__(self): | 102 | def __str__(self): |
| 59 | return self.command | 103 | return self.message |
| 60 | 104 | ||
| 61 | 105 | ||
| 62 | class UploadError(Exception): | 106 | class UploadError(RepoError): |
| 63 | """A bundle upload to Gerrit did not succeed.""" | 107 | """A bundle upload to Gerrit did not succeed.""" |
| 64 | 108 | ||
| 65 | def __init__(self, reason): | 109 | def __init__(self, reason, **kwargs): |
| 66 | super().__init__(reason) | 110 | super().__init__(reason, **kwargs) |
| 67 | self.reason = reason | 111 | self.reason = reason |
| 68 | 112 | ||
| 69 | def __str__(self): | 113 | def __str__(self): |
| 70 | return self.reason | 114 | return self.reason |
| 71 | 115 | ||
| 72 | 116 | ||
| 73 | class DownloadError(Exception): | 117 | class DownloadError(RepoExitError): |
| 74 | """Cannot download a repository.""" | 118 | """Cannot download a repository.""" |
| 75 | 119 | ||
| 76 | def __init__(self, reason): | 120 | def __init__(self, reason, **kwargs): |
| 77 | super().__init__(reason) | 121 | super().__init__(reason, **kwargs) |
| 78 | self.reason = reason | 122 | self.reason = reason |
| 79 | 123 | ||
| 80 | def __str__(self): | 124 | def __str__(self): |
| 81 | return self.reason | 125 | return self.reason |
| 82 | 126 | ||
| 83 | 127 | ||
| 84 | class NoSuchProjectError(Exception): | 128 | class SyncError(RepoExitError): |
| 129 | """Cannot sync repo.""" | ||
| 130 | |||
| 131 | |||
| 132 | class UpdateManifestError(RepoExitError): | ||
| 133 | """Cannot update manifest.""" | ||
| 134 | |||
| 135 | |||
| 136 | class NoSuchProjectError(RepoExitError): | ||
| 85 | """A specified project does not exist in the work tree.""" | 137 | """A specified project does not exist in the work tree.""" |
| 86 | 138 | ||
| 87 | def __init__(self, name=None): | 139 | def __init__(self, name=None, **kwargs): |
| 88 | super().__init__(name) | 140 | super().__init__(**kwargs) |
| 89 | self.name = name | 141 | self.name = name |
| 90 | 142 | ||
| 91 | def __str__(self): | 143 | def __str__(self): |
| @@ -94,11 +146,11 @@ class NoSuchProjectError(Exception): | |||
| 94 | return self.name | 146 | return self.name |
| 95 | 147 | ||
| 96 | 148 | ||
| 97 | class InvalidProjectGroupsError(Exception): | 149 | class InvalidProjectGroupsError(RepoExitError): |
| 98 | """A specified project is not suitable for the specified groups""" | 150 | """A specified project is not suitable for the specified groups""" |
| 99 | 151 | ||
| 100 | def __init__(self, name=None): | 152 | def __init__(self, name=None, **kwargs): |
| 101 | super().__init__(name) | 153 | super().__init__(**kwargs) |
| 102 | self.name = name | 154 | self.name = name |
| 103 | 155 | ||
| 104 | def __str__(self): | 156 | def __str__(self): |
| @@ -107,7 +159,7 @@ class InvalidProjectGroupsError(Exception): | |||
| 107 | return self.name | 159 | return self.name |
| 108 | 160 | ||
| 109 | 161 | ||
| 110 | class RepoChangedException(Exception): | 162 | class RepoChangedException(BaseRepoError): |
| 111 | """Thrown if 'repo sync' results in repo updating its internal | 163 | """Thrown if 'repo sync' results in repo updating its internal |
| 112 | repo or manifest repositories. In this special case we must | 164 | repo or manifest repositories. In this special case we must |
| 113 | use exec to re-execute repo with the new code and manifest. | 165 | use exec to re-execute repo with the new code and manifest. |
| @@ -118,7 +170,7 @@ class RepoChangedException(Exception): | |||
| 118 | self.extra_args = extra_args or [] | 170 | self.extra_args = extra_args or [] |
| 119 | 171 | ||
| 120 | 172 | ||
| 121 | class HookError(Exception): | 173 | class HookError(RepoError): |
| 122 | """Thrown if a 'repo-hook' could not be run. | 174 | """Thrown if a 'repo-hook' could not be run. |
| 123 | 175 | ||
| 124 | The common case is that the file wasn't present when we tried to run it. | 176 | The common case is that the file wasn't present when we tried to run it. |
