Skip to content

Commit

Permalink
Add knobs to control when and how dev-cmd exits.
Browse files Browse the repository at this point in the history
  • Loading branch information
jsirois committed Dec 29, 2024
1 parent e0cae8e commit 76608e2
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 150 deletions.
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Release Notes

## 0.7.0

Add knobs to control when and how `dev-cmd` exits an invocation that has command failures:
+ `[tool.dev-cmd] exit-style` and `-k` / `--keep-going` or `-X` / `--exit-style` allow configuring
how quickly the `dev-cmd` invocation exits after a command failure. By default, it exits after the
step containing the 1st command failure completes, but an immediate end or continuation through
all steps can be requested.
+ `[tool.dev-cmd] grace-period` and `--grace-period` allow configuring how in-flight commands are
terminated when exiting an invocation with command errors.

## 0.6.0

Emit a trailing message indicating overall run status and timing.
Expand Down
2 changes: 1 addition & 1 deletion dev_cmd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2024 John Sirois.
# Licensed under the Apache License, Version 2.0 (see LICENSE).

__version__ = "0.6.0"
__version__ = "0.7.0"
59 changes: 49 additions & 10 deletions dev_cmd/errors.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,61 @@
# Copyright 2024 John Sirois.
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import annotations

class DevError(Exception):
"""Indicates an error processing dev commands."""
import os
import shlex
import signal
from dataclasses import dataclass
from typing import Collection

from dev_cmd.model import Command

class InvalidProjectError(DevError):
"""Indicates the dev runner cannot locate or parse the `pyproject.toml` file."""

class DevCmdError(Exception):
"""Indicates an error processing `dev-cmd` configuration."""

class InvalidArgumentError(DevError):
"""Indicates invalid argument were passed to the dev command."""

class InvalidProjectError(DevCmdError):
"""Indicates the `dev-cmd` runner cannot locate or parse the `pyproject.toml` file."""

class InvalidModelError(DevError):
"""Indicates invalid dev command configuration."""

class InvalidArgumentError(DevCmdError):
"""Indicates invalid arguments were passed to `dev-cmd`."""

class ParallelExecutionError(Exception):
"""Conveys details of 2 or more failed parallel commands."""

class InvalidModelError(DevCmdError):
"""Indicates invalid `dev-cmd` configuration."""


@dataclass(frozen=True)
class ExecutionError(Exception):
"""Conveys details of 1 or more command failures in a `dev-cmd` invocation."""

@classmethod
def from_failed_cmd(cls, cmd: Command, exit_code: int) -> ExecutionError:
if exit_code < 0:
try:
reason = f"died with {signal.Signals(-exit_code).name}"
except ValueError:
reason = f"died with unknown signal {-exit_code}"
else:
reason = f"returned non-zero exit status {exit_code}"

return cls(step_name=cmd.name, message=f"Command `{shlex.join(cmd.args)}` {reason}")

@classmethod
def from_errors(
cls,
step_name: str,
total_count: int,
errors: Collection[ExecutionError],
parallel: bool = False,
) -> ExecutionError:
maybe_parallel = "parallel " if parallel else ""
lines = [f"{len(errors)} of {total_count} {maybe_parallel}commands in {step_name} failed:"]
lines.extend(f"{error.step_name}: {error.message}" for error in errors)
return cls(step_name=step_name, message=os.linesep.join(lines))

step_name: str
message: str
Loading

0 comments on commit 76608e2

Please sign in to comment.