diff options
Diffstat (limited to 'subcmds/grep.py')
| -rw-r--r-- | subcmds/grep.py | 84 |
1 files changed, 58 insertions, 26 deletions
diff --git a/subcmds/grep.py b/subcmds/grep.py index 5cd33763..9ebd776c 100644 --- a/subcmds/grep.py +++ b/subcmds/grep.py | |||
| @@ -17,8 +17,10 @@ import sys | |||
| 17 | 17 | ||
| 18 | from color import Coloring | 18 | from color import Coloring |
| 19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand | 19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand |
| 20 | from error import GitError | 20 | from error import GitError, InvalidArgumentsError, SilentRepoExitError |
| 21 | from git_command import GitCommand | 21 | from git_command import GitCommand |
| 22 | from typing import NamedTuple | ||
| 23 | from project import Project | ||
| 22 | 24 | ||
| 23 | 25 | ||
| 24 | class GrepColoring(Coloring): | 26 | class GrepColoring(Coloring): |
| @@ -28,6 +30,22 @@ class GrepColoring(Coloring): | |||
| 28 | self.fail = self.printer("fail", fg="red") | 30 | self.fail = self.printer("fail", fg="red") |
| 29 | 31 | ||
| 30 | 32 | ||
| 33 | class ExecuteOneResult(NamedTuple): | ||
| 34 | """Result from an execute instance.""" | ||
| 35 | |||
| 36 | project: Project | ||
| 37 | rc: int | ||
| 38 | stdout: str | ||
| 39 | stderr: str | ||
| 40 | error: GitError | ||
| 41 | |||
| 42 | |||
| 43 | class GrepCommandError(SilentRepoExitError): | ||
| 44 | """Grep command failure. Since Grep command | ||
| 45 | output already outputs errors ensure that | ||
| 46 | aggregate errors exit silently.""" | ||
| 47 | |||
| 48 | |||
| 31 | class Grep(PagedCommand): | 49 | class Grep(PagedCommand): |
| 32 | COMMON = True | 50 | COMMON = True |
| 33 | helpSummary = "Print lines matching a pattern" | 51 | helpSummary = "Print lines matching a pattern" |
| @@ -246,11 +264,18 @@ contain a line that matches both expressions: | |||
| 246 | bare=False, | 264 | bare=False, |
| 247 | capture_stdout=True, | 265 | capture_stdout=True, |
| 248 | capture_stderr=True, | 266 | capture_stderr=True, |
| 267 | verify_command=True, | ||
| 249 | ) | 268 | ) |
| 250 | except GitError as e: | 269 | except GitError as e: |
| 251 | return (project, -1, None, str(e)) | 270 | return ExecuteOneResult(project, -1, None, str(e), e) |
| 252 | 271 | ||
| 253 | return (project, p.Wait(), p.stdout, p.stderr) | 272 | try: |
| 273 | error = None | ||
| 274 | rc = p.Wait() | ||
| 275 | except GitError as e: | ||
| 276 | rc = 1 | ||
| 277 | error = e | ||
| 278 | return ExecuteOneResult(project, rc, p.stdout, p.stderr, error) | ||
| 254 | 279 | ||
| 255 | @staticmethod | 280 | @staticmethod |
| 256 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): | 281 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): |
| @@ -258,31 +283,40 @@ contain a line that matches both expressions: | |||
| 258 | bad_rev = False | 283 | bad_rev = False |
| 259 | have_match = False | 284 | have_match = False |
| 260 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) | 285 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) |
| 286 | errors = [] | ||
| 261 | 287 | ||
| 262 | for project, rc, stdout, stderr in results: | 288 | for result in results: |
| 263 | if rc < 0: | 289 | if result.rc < 0: |
| 264 | git_failed = True | 290 | git_failed = True |
| 265 | out.project("--- project %s ---" % _RelPath(project)) | 291 | out.project("--- project %s ---" % _RelPath(result.project)) |
| 266 | out.nl() | 292 | out.nl() |
| 267 | out.fail("%s", stderr) | 293 | out.fail("%s", result.stderr) |
| 268 | out.nl() | 294 | out.nl() |
| 295 | errors.append(result.error) | ||
| 269 | continue | 296 | continue |
| 270 | 297 | ||
| 271 | if rc: | 298 | if result.rc: |
| 272 | # no results | 299 | # no results |
| 273 | if stderr: | 300 | if result.stderr: |
| 274 | if have_rev and "fatal: ambiguous argument" in stderr: | 301 | if ( |
| 302 | have_rev | ||
| 303 | and "fatal: ambiguous argument" in result.stderr | ||
| 304 | ): | ||
| 275 | bad_rev = True | 305 | bad_rev = True |
| 276 | else: | 306 | else: |
| 277 | out.project("--- project %s ---" % _RelPath(project)) | 307 | out.project( |
| 308 | "--- project %s ---" % _RelPath(result.project) | ||
| 309 | ) | ||
| 278 | out.nl() | 310 | out.nl() |
| 279 | out.fail("%s", stderr.strip()) | 311 | out.fail("%s", result.stderr.strip()) |
| 280 | out.nl() | 312 | out.nl() |
| 313 | if result.error is not None: | ||
| 314 | errors.append(result.error) | ||
| 281 | continue | 315 | continue |
| 282 | have_match = True | 316 | have_match = True |
| 283 | 317 | ||
| 284 | # We cut the last element, to avoid a blank line. | 318 | # We cut the last element, to avoid a blank line. |
| 285 | r = stdout.split("\n") | 319 | r = result.stdout.split("\n") |
| 286 | r = r[0:-1] | 320 | r = r[0:-1] |
| 287 | 321 | ||
| 288 | if have_rev and full_name: | 322 | if have_rev and full_name: |
| @@ -290,13 +324,13 @@ contain a line that matches both expressions: | |||
| 290 | rev, line = line.split(":", 1) | 324 | rev, line = line.split(":", 1) |
| 291 | out.write("%s", rev) | 325 | out.write("%s", rev) |
| 292 | out.write(":") | 326 | out.write(":") |
| 293 | out.project(_RelPath(project)) | 327 | out.project(_RelPath(result.project)) |
| 294 | out.write("/") | 328 | out.write("/") |
| 295 | out.write("%s", line) | 329 | out.write("%s", line) |
| 296 | out.nl() | 330 | out.nl() |
| 297 | elif full_name: | 331 | elif full_name: |
| 298 | for line in r: | 332 | for line in r: |
| 299 | out.project(_RelPath(project)) | 333 | out.project(_RelPath(result.project)) |
| 300 | out.write("/") | 334 | out.write("/") |
| 301 | out.write("%s", line) | 335 | out.write("%s", line) |
| 302 | out.nl() | 336 | out.nl() |
| @@ -304,7 +338,7 @@ contain a line that matches both expressions: | |||
| 304 | for line in r: | 338 | for line in r: |
| 305 | print(line) | 339 | print(line) |
| 306 | 340 | ||
| 307 | return (git_failed, bad_rev, have_match) | 341 | return (git_failed, bad_rev, have_match, errors) |
| 308 | 342 | ||
| 309 | def Execute(self, opt, args): | 343 | def Execute(self, opt, args): |
| 310 | out = GrepColoring(self.manifest.manifestProject.config) | 344 | out = GrepColoring(self.manifest.manifestProject.config) |
| @@ -333,16 +367,14 @@ contain a line that matches both expressions: | |||
| 333 | have_rev = False | 367 | have_rev = False |
| 334 | if opt.revision: | 368 | if opt.revision: |
| 335 | if "--cached" in cmd_argv: | 369 | if "--cached" in cmd_argv: |
| 336 | print( | 370 | msg = "fatal: cannot combine --cached and --revision" |
| 337 | "fatal: cannot combine --cached and --revision", | 371 | print(msg, file=sys.stderr) |
| 338 | file=sys.stderr, | 372 | raise InvalidArgumentsError(msg) |
| 339 | ) | ||
| 340 | sys.exit(1) | ||
| 341 | have_rev = True | 373 | have_rev = True |
| 342 | cmd_argv.extend(opt.revision) | 374 | cmd_argv.extend(opt.revision) |
| 343 | cmd_argv.append("--") | 375 | cmd_argv.append("--") |
| 344 | 376 | ||
| 345 | git_failed, bad_rev, have_match = self.ExecuteInParallel( | 377 | git_failed, bad_rev, have_match, errors = self.ExecuteInParallel( |
| 346 | opt.jobs, | 378 | opt.jobs, |
| 347 | functools.partial(self._ExecuteOne, cmd_argv), | 379 | functools.partial(self._ExecuteOne, cmd_argv), |
| 348 | projects, | 380 | projects, |
| @@ -354,12 +386,12 @@ contain a line that matches both expressions: | |||
| 354 | ) | 386 | ) |
| 355 | 387 | ||
| 356 | if git_failed: | 388 | if git_failed: |
| 357 | sys.exit(1) | 389 | raise GrepCommandError( |
| 390 | "error: git failures", aggregate_errors=errors | ||
| 391 | ) | ||
| 358 | elif have_match: | 392 | elif have_match: |
| 359 | sys.exit(0) | 393 | sys.exit(0) |
| 360 | elif have_rev and bad_rev: | 394 | elif have_rev and bad_rev: |
| 361 | for r in opt.revision: | 395 | for r in opt.revision: |
| 362 | print("error: can't search revision %s" % r, file=sys.stderr) | 396 | print("error: can't search revision %s" % r, file=sys.stderr) |
| 363 | sys.exit(1) | 397 | raise GrepCommandError(aggregate_errors=errors) |
| 364 | else: | ||
| 365 | sys.exit(1) | ||
