Skip to content
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ Applications that need to perform work after an abort can use
`Command.run-for-exit-code`. It returns 1 after an abort instead of terminating
the process. Other exceptions continue unwinding.

To retain the legacy behavior of exiting immediately without unwinding, create
or copy a UI with `--exit-on-abort`, for example:

``` toit
ui := existing-ui.with --exit-on-abort
```

### Cache

The cache is a simple key-value store that persists between runs. Cached data may
Expand Down
38 changes: 23 additions & 15 deletions src/ui.toit
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import log as log-lib

ABORT-EXCEPTION_ ::= Object

create-ui-from-args_ args/List -> Ui:
create-ui-from-args_ args/List --exit-on-abort/bool=false -> Ui:
verbose-level/string? := null
output-format/string? := null

Expand Down Expand Up @@ -54,11 +54,11 @@ create-ui-from-args_ args/List -> Ui:
else: level = Ui.NORMAL-LEVEL

if output-format == "json":
return Ui.json --level=level
return Ui.json --level=level --exit-on-abort=exit-on-abort
else if output-format == "plain":
return Ui.plain --level=level
return Ui.plain --level=level --exit-on-abort=exit-on-abort
else if output-format == "human":
return Ui.human --level=level
return Ui.human --level=level --exit-on-abort=exit-on-abort
else:
throw "Invalid output format: $output-format"

Expand Down Expand Up @@ -275,23 +275,25 @@ class Ui:

level/int
printer_/Printer
exit-on-abort_/bool

constructor --.level=NORMAL-LEVEL --printer/Printer:
constructor --.level=NORMAL-LEVEL --printer/Printer --exit-on-abort/bool=false:
printer_ = printer
exit-on-abort_ = exit-on-abort
if not DEBUG-LEVEL >= level >= SILENT-LEVEL:
throw "Invalid level: $level"

constructor.human --level/int=NORMAL-LEVEL:
return Ui --level=level --printer=HumanPrinter
constructor.human --level/int=NORMAL-LEVEL --exit-on-abort/bool=false:
return Ui --level=level --printer=HumanPrinter --exit-on-abort=exit-on-abort

constructor.plain --level/int=NORMAL-LEVEL:
return Ui --level=level --printer=PlainPrinter
constructor.plain --level/int=NORMAL-LEVEL --exit-on-abort/bool=false:
return Ui --level=level --printer=PlainPrinter --exit-on-abort=exit-on-abort

constructor.json --level/int=NORMAL-LEVEL:
return Ui --level=level --printer=JsonPrinter
constructor.json --level/int=NORMAL-LEVEL --exit-on-abort/bool=false:
return Ui --level=level --printer=JsonPrinter --exit-on-abort=exit-on-abort

constructor.from-args args/List:
return create-ui-from-args_ args
constructor.from-args args/List --exit-on-abort/bool=false:
return create-ui-from-args_ args --exit-on-abort=exit-on-abort

/**
Returns the log-level (like $log.DEBUG-LEVEL) of this instance.
Expand Down Expand Up @@ -720,21 +722,27 @@ class Ui:
method should never return.
*/
abort -> none:
if exit-on-abort_: exit 1
throw ABORT-EXCEPTION_

/**
Returns a new Ui object with the given $level and $printer.
Returns a new Ui object with the given $level, $printer, and abort behavior.

If $level is not provided, the level of the new Ui object is the same as
this object.

If $printer is not provided, the printer of the new Ui object
is the same as this object.

If $exit-on-abort is not provided, the abort behavior of the new Ui object
is the same as this object.
*/
with --level/int?=null --printer/Printer?=null -> Ui:
with --level/int?=null --printer/Printer?=null --exit-on-abort/bool?=null -> Ui:
if exit-on-abort == null: exit-on-abort = exit-on-abort_
return Ui
--level=level or this.level
--printer=printer or this.printer_
--exit-on-abort=exit-on-abort

class HumanPrinter extends HumanPrinterBase:
print_ str/string:
Expand Down
29 changes: 28 additions & 1 deletion tests/abort_test.toit
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@

import cli
import expect show *
import fs
import host.pipe
import system

main args:
if args == ["exit-on-abort"]:
test-immediate-exit-child
return

main:
test-success
test-abort
test-parser-abort
test-other-exception
test-immediate-exit

test-success:
command := cli.Command "app" --run=:: null
Expand Down Expand Up @@ -40,3 +48,22 @@ test-other-exception:
command := cli.Command "app" --run=:: throw "OTHER"
exception := catch: command.run-for-exit-code []
expect-equals "OTHER" exception

test-immediate-exit:
test-dir := fs.dirname system.program-path
exit-code := pipe.run-program [
"toit",
"run",
"--project-root=$test-dir",
system.program-path,
"--",
"exit-on-abort",
]
expect-equals 1 exit-code

test-immediate-exit-child:
ui := (cli.Ui.human --level=cli.Ui.SILENT-LEVEL).with --exit-on-abort
try:
ui.abort "Stop immediately."
finally:
print "Immediate abort unexpectedly unwound."
Loading