2
$\begingroup$

I am trying to set up a Mathematica script so that it runs from the command line in the manner that a sane person would expect. I have the following at the start of the file:

#!/usr/local/bin/MathKernel -script

which lets me run my script from the console like so

./<my_code>.m <arguments>

(after chmod-ing it) and I retrieve the various arguments like so:

args = Join[$CommandLine[[4;;]],Table[{},{i,1,10}]];

(I have 4 arguments; I forget why the Table is 10 slots long, I must have had a reason for that...) and then I can use them like

somevariable = args[[1]];

and so on. Another innovation I learned of (from this question: How to abort on any message generated?) is the following:

messageHandler = If[Last[#], Print["An error has occurred in '<my_code>.m'; aborting evaluation..."] Exit[1];]&
Internal`AddHandler["Message", messageHandler]

which I use to make the script abort when something goes wrong, to prevent it going completely haywire as Mathematica is wont to do.

So now to the next step; when the script dies due to some problem occurring (as picked up by the above messageHandler), I would like to know what line number of my input file caused the problem. I found the following comment on an old mailing list (http://forums.wolfram.com/mathgroup/archive/2008/Jul/msg00091.html)

Aaron Fude wrote:

Hi,

Mathematica is very good at reporting that you've made an error, but it doesn't tell you where the error occurred. Is there a way to obtain the "stacktrace"?

Many thank in advance!

Aaron

to which the only reply was

Hi Aaron,

if you write your code in a *.m file and read it in, Mathematica will report the error line number.

Daniel

So ok it sounds like Mathematica can do it somehow, but it sure doesn't do it by default when I run my script from the command line rather than import it into a notebook. So is there a way to "switch on" such behaviour? I also found the following question on the topic: Finding a Specific Line in a Package but I don't quite see how to apply that to my scenario, and it seems like it might be overkill anyway.

Lots of hassle to get sensible batch job behaviour! I have further issues trying to get it to print output in a readable/parsable one-line format (rather than all the stupid multi-line formatting that it does by default for fractions etc; see Set form of all output to InputForm) but perhaps that is for another question...

edit: Ok, it seems like sometimes Mathematica does decide to give me a line number for the error, e.g.:

Syntax::sntx: Invalid syntax in or before "If[Count[validiso,isotope]==0], " (line 44 of "./recoil_generator.m").

which is nice, but it doesn't do it all the time.

$\endgroup$
  • $\begingroup$ Mathematica will report the line number of a syntax error. It cannot report the line number of an arbitrary Message because there's no direct link between the in-memory program and the source file. In most of the cases there is no source file at all. Definitions can be issued from the command line, or created completely programmatically. Looking at the title of the question my first reaction would be to say, "inherently impossible". But you are not asking only for line numbers but also for some sort of stack trace, which should be possible. Therefore I suggest re-titling the question. $\endgroup$ – Szabolcs Feb 10 '16 at 13:09
  • $\begingroup$ Hmm, interesting, I had assumed that a line number would be easier to get (and is good enough for me) than some kind of stack trace so I focused on that, but ok, if I cannot get a line number then just anything to help locate the problem would be great. Title adjusted. $\endgroup$ – Ben Farmer Feb 10 '16 at 13:14
  • $\begingroup$ Check this out. (The presentation is about future features.) $\endgroup$ – Szabolcs Feb 17 '16 at 19:49
2
$\begingroup$

Mathematica will report the line number of syntax errors when reading the code from a file. These errors come directly from the parser, not from the evaluator.

Once the code is read and evaluated, any links between any source files and the in-memory definitions are gone. It is inherently impossible to report the location of messages as a line number because there is no direct link between a source file and the in-memory program.

Most computer languages, even high-level scripting languages, encourage a source-file based workflow. First type your program in a source file, then run the file. Mathematica is different: most of the time code is evaluated interactively, and there is no source file at all. Definitions can be easily issued (or modified) programmatically, so there is no good way to associate textual input lines with the actual code.

The built-in debugger suffers from this problem too: it does attempt to make this association. For example, it allows setting breakpoints directly in the code that was typed in the notebook, and when evaluation is interrupted, it attempts to highlight the piece of code where evaluation has stopped. This will inevitably fail in many cases because during a typical workflow code in the notebook changes continually, and the link between the code and the in-memory program always gets broken. In my opinion, this is the single major source of the builtin debugger's bad reputation (which I don't agree with). I do find the debugger quite useful for as long as I avoid trying to treat source code as something that directly corresponds to the in-memory program. That is why I advocate not using breakpoints at all, but instead triggering a break using an Assert inserted directly in the code.

Why do I find the debugger useful? One of the big reasons is that it shows the stack and it allows inspecting local variables. So it is possible to show a stack trace, and determine the location where the message was issued! How can you do this in your script? I would modify the message handler like this:

messageHandler = StackInhibit@If[Last[#],
    Print@Stack[];
    Print["Aborting evaluation..."];
    Exit[1];] &

Just print Stack[] or Stack[_]. StackInhibit simply prevents the messageHandler from being included in the trace, and can safely be dropped.

This is how it works:

Mathematica graphics

It will show that the message was issued from inside Times used in f, which is in turn used in Module.

Finally, note that one big caveat with aborting on any message is that a message is generally not an error. Messages are used for many purposes, just as warnings or showing useful information.


Summary:

  • Try to adopt a workflow which does not rely on line numbers and is not centred on source files. (Of course there are many situations when scripts are useful or necessary, but it should not be the main way you use Mathematica.)

  • You can just Print the Stack in the message handler.

  • Be careful not to inadvertently abort the script on harmless messages.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.