Thursday, July 24, 2014

#1 2006-10-16 04:00:43 am

Craig Smith
Administrator
From: Tacoma, WA
Registered: 2005-05-16
Posts: 983
Website

AppleScript for Beginners VII - Errors

Well, I suppose it finally had to happen.  If you have been reading along with these tutorials, you know that we have already had just about as much fun as we are going to have, and it the time has come to start dealing with the dark side of AppleScript.  I remember when I first ventured into to the realm of increasingly complex script writing, and the difficulties I had finding out why the heck this script wasn't working the way I wanted it to work.  Of course, I can't read your mind and have no clue what sort of crazy things you are going to write in your scripts, so we will focus on the general methods to try to solve problems and find the bugs.  As usual, let's start with our final script from last time:

Applescript:


tell application "iTunes"
   set five_tracks to {}
   repeat 5 times
       try
           set end of five_tracks to some track of library playlist
       on error errTxt number errNum
           display dialog errTxt & return & errNum
       end try
   end repeat
   try
       play some item of five_tracks
   on error errTxt number errNum
       display dialog errTxt & return & errNum
   end try
end tell

I hope that you had a chance to play with this a little bit, and I REALLY hope that you found the error and discovered the missing word to fix the script. Just in case you have not, please run the script and note that you get the following error:

Can't get some track of library playlist
-1728


Well, that's interesting, isn't it? I mean, sure, it is nearly nonsense, but it is interesting. In fact, there is enough information in this dialog to pinpoint the precise line of AppleScript code where that nasty -1728 error was generated. Let's start by looking at the script itself. You see that all the code is contained within an iTunes tell block, and then there is a repeat block, that itself contains what is called a try block. The try block is one of the simplest, yet most effective methods of finding, trapping, and getting specific information about errors in your scripts. Basically, whenever an error occurs within a try block, that portion of the script stops, and the script continues to execute, starting right after the end try statement. In our case, the first action we try to do is this line:

Applescript:


set end of five_tracks to some track of library playlist

which then generates the error that alludes to it, by directly stating that the script can't get some track of library playlist. This tells us where the error is, and the number tells us what the error is. (We will get to the numbers soon enough, hang on for now.) You need to know about the real power of the try block that presents that information to us before we continue. That power is contained in the line that states:

Applescript:


on error errTxt number errNum

This statement activates an error handler that will gather whatever information is available about the error that occurred within the try block. I have never covered handlers in this series, and I have no intention of doing so, since there are already some superb tutorials on this site that do the job already. I highly recommend this one first, and this one second, and finally, this one third. You don't need to stop now and read them, but bookmark them or print them out for later perusal. If you have made it this far in my series, you are definitely ready to learn the amazing abilities of handlers.

For now, just know that a handler is like a sub-routine. It is sort of a mini-program, or mini-script within your script (or the system) that can be called from your script to do a specific task, after which the script continues on its merry way from the point where you called the handler. In this case, the error handler is part of the system, and you call it by simply stating on error within your try block. Most errors will return an error message in the form of a string, and an error number. The syntax I used above is how you extract those items when your script hits an error; the variable <font color="#ff0000">errTxt</font> will hold the error message, and the variable errNum will hold the error number. (You can, of course, make whatever variables you wish for this.) In my case, I then use a display dialog box to show the message and the number:

Applescript:


display dialog errTxt & return & errNum

Although I will not go into any real details here, you can do whatever you want in that on error section, if you believe that it will help you to discover what went wrong with the execution of the script. If you are curious about what sorts of errors are out there, this page outlines many of the AppleScript errors that can pop up, and this page lists a multitude of Apple Event errors that you may see. Neither place has much more information than mere lists, but they are interesting nonetheless.

Heading back to our current script, you see that I have another try block in the latter half of the script, right after the end return. If you run the script, and click OK on each dialog box that comes up while the script is cycling through the repeat loop 5 times, you will get a sixth dialog box that displays this information:

Can't get some item of {}.
-1728

It is the same error number, but with a different message, indicating in this case that we are trying to get some item of an empty list ({}). That would be a bit harder to track down in a long script, so let's change the on error commands to this:

Applescript:


display dialog errTxt & return & errNum & return & "In the second try block."

This time, when the error is generated, we not only get the information on what the error is, we also get some information about where in the script we need to look to find the error.

I think you have waited long enough to know how to fix this baby if you have not discovered it already. In the first half, the error about Can't get some track of library playlist should be a giveaway that when we asked iTunes to build a list of 5 tracks, we just said library playlist instead of the correct form first library playlist or library playlist 1, both of which indicate the entire collection of music within iTunes. So, simply by changing that line in our script to read:

Applescript:


set end of five_tracks to some track of library playlist 1

we have fixed the script, and it now functions perfectly, playing one of the items in the list of 5 tracks, no more errors. That is the basis of de-bugging via the try block. Sometimes, one needs to put the entire script within a try block, and hope that the error message it throws will make some sense as to where the problem lies. Other times (although it requires more effort) it is advisable to place a few try blocks in your script, with proper labels in your display dialogs, to isolate just where something is being messed up.

There are other uses for try blocks that you may find useful. For instance, let's say you have a pathological fascination with numbers, and you cannot sleep until you know how a huge bunch of numbers would correspond to tracks in your iTunes library. Your head is filled with a ton of numbers, and you wonder which tracks they represent in your library, so you put together a stupid script:

Applescript:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
   repeat with a_number in a_bunch_of_numbers
       try
           set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
       on error
           set err_List to err_List & "There is no track number " & a_number & " in your iTunes library" & return
       end try
   end repeat
   set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

In this instance, only two possibilities exist for each number in your list. There is either a track with that number in your library, or this is not. We use the try block to find them (instead of an if/then/else) knowing that if the track exists, its name will be added to the track_Names list. If it does not exist, an error will occur, which will call the on error handler, and thus build the string that will be eventually displayed in a TextEdit document, along with the list of successfully located track names.

As you can see, try blocks are versatile and useful for both locating errors, and just plain sorting through data. As with just about any part of AppleScript, it represents just another way of doing something that certainly could be accomplished with other means (as in our last script), but brings along enough unique tools to make it powerful and useful. And, its usage is only limited by your imagination (or lack thereof). Let's say that you are POSITIVE that you have written something that will snag every error, and return the information to you within your on error handler, but you are not convinced. Well, genius, why not put a try block within the error handler itself? In the script just above, how about if a particular number doesn't exist in your iTunes library, so you decide to split the number in half? We know that won't work, since some of the numbers are odd, but go through the exercise anyway, and look at this iteration:

Applescript:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
   repeat with a_number in a_bunch_of_numbers
       try
           set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
       on error
           try
               set end of track_Names to name of (track (a_number / 2) of first library playlist) & " - " & a_number & return
           on error
               set err_List to err_List & "There is no track number " & (a_number / 2) & " in your iTunes library" & return
           end try
       end try
   end repeat
   set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

We can now catch the errors that are thrown when something goes to the on error handler as well. And yes, you could just keep going and going, nesting more try blocks inside of on error handlers for as long as you could stand to sit at your terminal messing around. The point here is to remember to think creatively in all your scripting. If you are not sure something will work, try it.


Craig Smith

Offline

 

#2 2013-01-28 02:29:49 pm

Paddypatpat
Member
Registered: 2013-01-27
Posts: 1

Re: AppleScript for Beginners VII - Errors

Hi there,

Thank you for this article. I'm doing my best to learn from it.

The links you provided to good articles covering handlers are not working (at least for me). Would you please check that these documents haven't moved somewhere else?

Thanks,

Patrick


Filed under: Handlers, off-topic

Offline

 

#3 2013-04-02 08:15:56 am

McUsrII
Member
Registered: 2012-11-20
Posts: 2254
Website

Re: AppleScript for Beginners VII - Errors

I hope this isn't appropriate or anything, but a lot of scripting deals with either handling errors, or figuring out what is going wrong. Now, figuring out what is going wrong, is reasonably easily easy when you are running your scripts from within AppleScript Editor, or Script Debugger, or XCode for that matter.

The approach above with dialog boxes, is of course the first line of deducing what is going wrong, when you can't run your script in an editor. Maybe you are creating an applet, or an automator service, and the error doesn't appear every time. Then what?

Well, I have a handler here, below, that I didn't write, but it is tremendously useful. It takes to parameters;

* A string containing the error condition: e & n inside on error e number n is perfectly good.

* The name of a logfile, this logfile can be obtained from the sidebar of the console app, under ~/Library/logs

It is a good idea to allways put "my" in front of the handler, as you then assure that your script will find the handler, provided it is located within the script that has the handler that calls it.

Applescript:


try
   tell application "Finder"
       if exists folder "Desk top" of home then
           set tvar to true
       else
           set tvar to false
       end if
   end tell
on error e number n
   my logit("Can't find desktop:" & e & " " & n, "errlogdemo")
end try

to logit(log_string, log_file)
   do shell script ¬
       "echo `date '+%Y-%m-%d %T: '`\"" & log_string & ¬
       "\" >> $HOME/Library/Logs/" & log_file & ".log"
end logit


Filed under: Error, logging

Offline

 

#4 2013-04-02 09:07:41 am

StefanK
Member
From: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 10454
Website

Re: AppleScript for Beginners VII - Errors

McUsrII wrote:

It is a good idea to allways put "my" in front of the handler,

I don't agree, it's a better idea to use application tell blocks only for code which uses the respective application terminology, for example

Applescript:


tell application "Finder" to set desktopExists to exists desktop
if desktopExists then
   doSomething()
else
   doSomethingElse()
end if

The rule to use the keyword my in front of a handler is very simple:
It's only required inside any tell block


regards

Stefan

Offline

 

#5 2013-04-02 09:55:19 am

McUsrII
Member
Registered: 2012-11-20
Posts: 2254
Website

Re: AppleScript for Beginners VII - Errors

Hello.

smile  The example, was contrived, as it was just a usage  example, how to use the handler.

The reason for my recommendation of prepending with "my", is that the milage varies, so when you are swimming around in new terms, and really need to put an effort into making a script work, then it is better to have some extranous "my"'s and a script that doesn't fail when calling the log handler inside a tell block,

And it is not only useful for tracking errors, you can also use the handler for following the program logic, It is always good to see what actually happens.


Filed under: Error, logging

Offline

 

#6 2013-04-13 02:52:14 am

alastor933
Member
From: Utrecht, NL
Registered: 2008-09-12
Posts: 441

Re: AppleScript for Beginners VII - Errors

Paddypatpat wrote:

The links you provided to good articles covering handlers are not working...

Hi Patrick,
Yes, they are from a previous incarnation of Macscripter. At the end of those URLs is a number. The number is still correct, but the rest of the URL has changed. Just open a page for another thread, and replace the number at the end of its URL with the number from the broken link.

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.039 seconds, 10 queries executed ]

RSS (new topics) RSS (active topics)