Skip to content
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

Add Process.run ?is_success to control definition of success #586

Merged
merged 1 commit into from
Jul 20, 2023
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
12 changes: 6 additions & 6 deletions lib_eio/process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ end
let pid proc = proc#pid
let await proc = proc#await

let await_exn proc =
let await_exn ?(is_success = Int.equal 0) proc =
match proc#await with
| `Exited 0 -> ()
| `Exited code when is_success code -> ()
| status -> raise (err (Child_error status))

let signal proc = proc#signal
Expand Down Expand Up @@ -84,26 +84,26 @@ let spawn ~sw (t:#mgr) ?cwd ?stdin ?stdout ?stderr ?env ?executable args =
?stdout:(stdout :> Flow.sink option)
?stderr:(stderr :> Flow.sink option)

let run (t:#mgr) ?cwd ?stdin ?stdout ?stderr ?env ?executable args =
let run (t:#mgr) ?cwd ?stdin ?stdout ?stderr ?(is_success = Int.equal 0) ?env ?executable args =
Switch.run @@ fun sw ->
let child = spawn ~sw t ?cwd ?stdin ?stdout ?stderr ?env ?executable args in
match await child with
| `Exited 0 -> ()
| `Exited code when is_success code -> ()
| status ->
let ex = err (Child_error status) in
raise (Exn.add_context ex "running command: %a" pp_args args)

let pipe ~sw (t:#mgr) = t#pipe ~sw

let parse_out (t:#mgr) parse ?cwd ?stdin ?stderr ?env ?executable args =
let parse_out (t:#mgr) parse ?cwd ?stdin ?stderr ?is_success ?env ?executable args =
Switch.run @@ fun sw ->
let r, w = pipe t ~sw in
try
let child = spawn ~sw t ?cwd ?stdin ~stdout:w ?stderr ?env ?executable args in
Flow.close w;
let output = Buf_read.parse_exn parse r ~max_size:max_int in
Flow.close r;
await_exn child;
await_exn ?is_success child;
output
with Exn.Io _ as ex ->
let bt = Printexc.get_raw_backtrace () in
Expand Down
13 changes: 11 additions & 2 deletions lib_eio/process.mli
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ val pid : #t -> int
val await : #t -> exit_status
(** [await t] waits for process [t] to exit and then reports the status. *)

val await_exn : #t -> unit
(** Like {! await} except an exception is raised if the status is not [`Exited 0]. *)
val await_exn : ?is_success:(int -> bool) -> #t -> unit
(** Like {! await} except an exception is raised if does not return a successful
exit status.

@param is_success Used to determine if an exit code is successful.
Default is [Int.equal 0]. *)

val signal : #t -> int -> unit
(** [signal t i] sends the signal [i] to process [t].
Expand Down Expand Up @@ -113,12 +117,16 @@ val run :
?stdin:#Flow.source ->
?stdout:#Flow.sink ->
?stderr:#Flow.sink ->
?is_success:(int -> bool) ->
?env:string array ->
?executable:string ->
string list -> unit
(** [run] does {!spawn} followed by {!await_exn}, with the advantage that if the process fails then
the error message includes the command that failed.

When [is_success] is provided, it is called with the exit code to determine whether it indicates success or failure.
Without [is_success], success requires the process to return an exit code of 0.

Note: If [spawn] needed to create extra fibers to copy [stdin], etc, then it also waits for those to finish. *)

val parse_out :
Expand All @@ -127,6 +135,7 @@ val parse_out :
?cwd:#Fs.dir Path.t ->
?stdin:#Flow.source ->
?stderr:#Flow.sink ->
?is_success:(int -> bool) ->
?env:string array ->
?executable:string ->
string list -> 'a
Expand Down
22 changes: 22 additions & 0 deletions tests/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,28 @@ Eio.Io Process Child_error Exited (code 3),
running command: bash -c "exit 3" "" foo
```

Exit code success can be determined by is_success (Process.run):

```ocaml
# run @@ fun mgr env ->
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 3"];;
- : unit = ()

# run @@ fun mgr env ->
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 0"];;
Exception:
Eio.Io Process Child_error Exited (code 0),
running command: bash -c "exit 0"
```

Exit code success can be determined by is_success (Process.parse_out):

```ocaml
# run @@ fun mgr env ->
Process.parse_out ~is_success:(Int.equal 5) mgr Eio.Buf_read.line ["sh"; "-c"; "echo 123; exit 5"];;
- : string = "123"
```

The default environment:

```ocaml
Expand Down
Loading