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

Allow inspecting cause of exceptions #208

Closed
xiaq opened this issue Sep 11, 2016 · 12 comments
Closed

Allow inspecting cause of exceptions #208

xiaq opened this issue Sep 11, 2016 · 12 comments
Milestone

Comments

@xiaq
Copy link
Member

@xiaq xiaq commented Sep 11, 2016

Errors are now opaque values. One should be able to inspect them by indexing into relevant fields. For instance, the following may be used to retrieve the exit code of an external command:

try {
  ls
} except e {
  println "exited with "$e[exit-code]
}
@xiaq xiaq added the enhancement label Sep 11, 2016
@xiaq xiaq added this to the 1.0 milestone Sep 11, 2016
@xiaq xiaq changed the title Allow inspecting errors Allow inspecting exceptions Feb 16, 2017
@xiaq xiaq modified the milestones: 1.0, 0.12 Jan 15, 2018
xiaq added a commit that referenced this issue Mar 8, 2018
@xiaq xiaq added the A:Language label Mar 18, 2018
@xiaq xiaq removed this from the 0.13 milestone Apr 6, 2019
@xiaq xiaq removed the type:enhancement label Oct 18, 2019
@xiaq xiaq added C:Introspection and removed A:Language labels Dec 27, 2019
@hanche
Copy link

@hanche hanche commented Jan 13, 2020

Related – perhaps it should have been a separate issue, but here goes:

I'd like to be able to re-throw an exception after capturing it, either via the ?(…) construct or the except block of a try statement.

Suggestion:

  • If $e is a caught exception, fail $e should re-throw it.
  • however, fail $ok should do nothing at all, both in order to save the user a conditional and in order to avoid dealing with the dreaded “no error” error.
krader1961 added a commit to krader1961/elvish that referenced this issue Mar 5, 2020
This also explicitly documents `float64` as a data type, although that
is a byproduct of this change. I created this change because someone
asked on the project's Gitter channel how to extract useful info from an
exception object.

Related elves#208
krader1961 added a commit to krader1961/elvish that referenced this issue Mar 5, 2020
This also explicitly documents `float64` as a data type, although that
is a byproduct of this change. I created this change because someone
asked on the project's Gitter channel how to extract useful info from an
exception object.

Related elves#208
krader1961 added a commit to krader1961/elvish that referenced this issue Mar 11, 2020
This also explicitly documents `float64` as a data type, although that
is a byproduct of this change. I created this change because someone
asked on the project's Gitter channel how to extract useful info from an
exception object.

Related elves#208
krader1961 added a commit to krader1961/elvish that referenced this issue Mar 23, 2020
This documents all numeric literal forms. As of Go 0.13 octal and binary
numeric literals are supported. Which means that Elvish supports such
literals. Since Go 0.13 is now the minimum Go version document those
numeric literals.

It also adds a related section that documents the existence of an exception
data type but punts a detailed description to an existing section on how
exceptions work.

Fixes elves#614
Related elves#208

Address PR review feedback
krader1961 added a commit to krader1961/elvish that referenced this issue Mar 31, 2020
This documents all numeric literal forms. As of Go 0.13 octal and binary
numeric literals are supported. Which means that Elvish supports such
literals. Since Go 0.13 is now the minimum Go version document those
numeric literals.

It also adds a related section that documents the existence of an exception
data type but punts a detailed description to an existing section on how
exceptions work.

Fixes elves#614
Related elves#208

Address PR review feedback
xiaq added a commit that referenced this issue Mar 31, 2020
This documents all numeric literal forms. As of Go 0.13 octal and binary
numeric literals are supported. Which means that Elvish supports such
literals. Since Go 0.13 is now the minimum Go version document those
numeric literals.

It also adds a related section that documents the existence of an exception
data type but punts a detailed description to an existing section on how
exceptions work.

Fixes #614
Related #208

Address PR review feedback
@xiaq xiaq added this to the 0.14 milestone Apr 17, 2020
@xiaq
Copy link
Member Author

@xiaq xiaq commented Apr 25, 2020

A comprehensive design is not there yet, but at least it should also be possible to access the message inside an exception raised by fail (see #986).

@xiaq xiaq changed the title Allow inspecting exceptions Allow inspecting cause of exceptions May 26, 2020
@xiaq
Copy link
Member Author

@xiaq xiaq commented May 26, 2020

Exposing the traceback is a bit hairy, so for the sake of this issue I'll focus on just the cause. Exposing the traceback is tracked in #1042.

@xiaq
Copy link
Member Author

@xiaq xiaq commented May 26, 2020

Design notes:

  • Add predicates in the exc: for determining the type of the cause, e.g. exc:is-external-cmd-exit (may want to rename this; it's too long). Internal structured errors such as errs.OutOfRange can have similar predicates.

  • Add fields for the data contained in the exception, e.g. $e[exit-code] for the numerical exit code (only present if exc:is-external-cmd-exit $e is true).

There is an open question regarding how to expose the many methods of Go's WaitStatus. There are several options:

  • Expose them as fields of the exception too.

  • Expose them as functions.

  • Expose them as methods; Elvish doesn't have the concept of methods today, so this will be blocked on Elvish having methods in the first place.

@hanche
Copy link

@hanche hanche commented May 27, 2020

I think exc:is-exit-code will work. Only external commands produce exit codes anyway; there is no need to spell it out. Also exc:is-signal. For the latter, it would be good to get both the signal number and the name.

@xiaq
Copy link
Member Author

@xiaq xiaq commented May 28, 2020

After researching a bit more on how to name this: "exit" and "exit code" actually have narrower meanings than I thought (at least on the POSIX C API level). A process exits only when it terminates on its own. If it was killed by a signal, it never exited, and there is no exit code, only a signal number.

POSIX uses 128+N to signal a command was killed by a signal; Elvish could distinguish the two different cases by giving them different predicates (that seems to be what @hanche is suggesting). I'd pick different names though; exit-code and signal are not really exceptional conditions themselves; I'd use exc:is-nonzero-exit and exc:is-killed.

@krader1961
Copy link
Contributor

@krader1961 krader1961 commented May 28, 2020

POSIX uses 128+N to signal a command was killed by a signal;

Note that is a POSIX shell (mis)feature. It is not a feature of the underlying kernel API. In UNIX the exit status is in the range [0, 255]. Exiting due to a signal is encoded in a fashion that ensures there is no ambiguity between SIGHUP (signal #1) and the program calling exit(129). Specifically, the exit code (if any) is left shifted eight bits by the kernel and or'ed with status bits and the terminating signal number (if any) in the low eight bits. This is an area where elvish can do better than the POSIX shell standard.

@hanche
Copy link

@hanche hanche commented May 29, 2020

man 2 wait to see what that looks like in C. I haven't quite learned my way around the go documentation yet; searching for wait just seems to give me bits of source code, which is fine if you enjoy the Obi-Wan Stallman method of documentation (“use the Source, Luke!”).

xiaq added a commit that referenced this issue May 30, 2020
xiaq added a commit that referenced this issue May 31, 2020
xiaq added a commit that referenced this issue May 31, 2020
These predicates are for exceptions, not errors. Moreover, Elvish doesn't really
have a concept of errors, only exceptions.

This addresses #208.
@krader1961
Copy link
Contributor

@krader1961 krader1961 commented Jun 17, 2020

Also, see this statement from https://en.wikipedia.org/wiki/Exit_status:

For the shell’s purposes, a command which exits with a zero exit status has succeeded. A nonzero exit status indicates failure. This seemingly counter-intuitive scheme is used so there is one well-defined way to indicate success and a variety of ways to indicate various failure modes. When a command is terminated by a signal whose number is N, a shell sets the variable $? to a value greater than 128. Most shells use 128+N, while ksh93 uses 256+N.

The POSIX 1003.2 shell standard more or less says the same thing. However, both documents are ambiguous. I find this situation particularly hilarious given how many fans of the ksh shell assert that it is 100% POSIX compliant. Ksh complies with the POSIX convention regarding the special exit statuses "126" and "127" but not the POSIX mandate to confuse an exit status in the range [128..255] with a termination due to a signal. At least with respect to the magic $? shell variable.

The bottom line is that it is impossible for Elvish to do the right thing, all of the time, with respect to exit statuses. One option is to follow the path I established for the Meson build system. See mesonbuild/meson@5290f41. However, I recommend that Elvish not attempt to handle shells that munge the exit status of the last external command they run. Just map the native API to an Elvish exception object. Don't attempt to divine (i.e., discover by guesswork)

a) if the external command really exited due to a signal as reported by the OS, or

b) exited by a signal because the external command was a shell that munged the exit status of the external command it ran.

@zzamboni
Copy link
Contributor

@zzamboni zzamboni commented Jun 30, 2020

It would be good sometimes to be able to query the exit code of the last command executed, without the need to capture an exception for it. My specific current use case is the implementation of iTerm2's Shell Integration features for Elvish, which among other things allow displaying a prompt marker which can indicate via its color the status of the last command. My current implementation is at https://github.com/zzamboni/elvish-modules/blob/master/iterm2.org, which works, but the marker is always displayed with the same color because there is no way to obtain this information.

I'm sure other use cases can be identified. Maybe something like $edit:last-command-status could be useful?

@xiaq
Copy link
Member Author

@xiaq xiaq commented Jun 30, 2020

@zzamboni for that use case, I'd introduce an API for a default exception handler that is run interactively.

@xiaq
Copy link
Member Author

@xiaq xiaq commented Jun 30, 2020

@zzamboni That is now #1059.

@xiaq xiaq closed this in 1f56250 Jun 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.